入门OJ:photo

博客围绕N个人来自K个家族排队照相问题展开。因排外性,需计算最少去掉多少人可让同家族人站一起。分析了对每个人留下或删去的情况,设计出状态dp(i,j,k),得出状态转移方程,时间复杂度为O(NK * 2^K)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

有N个人,来自K个家族.他们排成一行准备照相,但是由于天生的排外性,每个人都希望和本家族的人站在一起,中间不要加入别的家族的人.问最少从队列中去掉多少个就可以达到这个目的.

输入格式

第一行给出N,K。N在[1,100],K在[1,5]

第二行给出N个数,每个数为1到K中的某个数。

输出格式

最少从队列中去掉多少个就可以达到这个目的


显然对于当前第i个点我们可以做两个操作:留下或者删去。

如果留下,就会有两种情况:第i个人和上一个留下的人属于同一个家族。第二种情况是不属于同一个家族,那么我们的状态需要从当前有人留下的家族转移过来。
如果删去则不需要考虑任何东西。

综合上面的分析,可以知道我们需要的信息有:当前是第几个人,当前选了那些家族,处理完当前人之后最后一个留下的人的家族。

那么可以设计出这样的状态:dp(i,j,k),表示当前为第i个人,当前选的家族情况的二进制表示为j,最后一个留下的人的家族为k。设第i个人的家族为col,那么可以得出状态转移方程:

\[ dp[i][j][col]=Max_{1≤k≤K}{\{}dp[i-1][j{\wedge}R(1<<col)][k]+1{\}} \]

初始化都为0,答案为n-Max{dp(n,i,j)}

时间复杂度为O(NK * 2^K)。

#include<iostream>
#include<cstring>
#include<cstdio>
#define maxn 101
#define maxm 6
using namespace std;
 
int dp[maxn][1<<maxm][maxm];
int n,m,ans;
inline int read(){
    register int x(0),f(1); register char c(getchar());
    while(c<'0'||'9'<c){ if(x=='-') f=-1; c=getchar(); }
    while('0'<=c&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
 
int main(){
    //freopen("photo.in","r",stdin);
    //freopen("photo.out","w",stdout);
    n=read(),m=read();
     
    for(register int i=1;i<=n;i++){
        int col=read()-1;
        memcpy(dp[i],dp[i-1],sizeof dp[i-1]);
        for(register int j=0;j<1<<m;j++) if(j&(1<<col)){
            dp[i][j][col]=max(dp[i][j][col],dp[i-1][j][col]+1);
            for(register int k=0;k<m;k++){
                dp[i][j][col]=max(dp[i][j][col],dp[i-1][j^(1<<col)][k]+1);
            }
        }
    }
    for(register int i=0;i<1<<m;i++){
        for(register int j=0;j<m;j++){
            ans=max(ans,dp[n][i][j]);
        }
    }
    printf("%d\n",n-ans);
    return 0;
}

转载于:https://www.cnblogs.com/akura/p/11045948.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值