Luogu P3694 邦邦的大合唱站队
题目描述
个偶像排成一列,他们来自
个不同的乐队(
,
)。每个团队至少有一个偶像。
现在要求重新安排队列,使来自同一乐队的偶像连续的站在一起。重新安排的办法是,让若干偶像出列(剩下的偶像不动),然后让出列的偶像一个个归队到原来的空位,归队的位置任意。
请问最少让多少偶像出列?
思路
首先,DP题的第一个难点就是判断它是否可以用DP解决。而我们通过题目可以观察到:题目解决的是每个组的排列问题,同时题目中的乐队数 非常的小,所以可以考虑用状压DP解决。
接下来,我们可以设 为状态为
时安排好队列所需的最小出列人数。然后,可以得到这样的状态转移方程:
f[i]=min(f[i],f[i^(1<<(j-1))]+num[j]-sum[len][j]+sum[len-num[j]][j]);
其中的 代表当前正在处理的乐队的编号,而
代表将乐队
中的第
个人的状态取反(
代表已出列,
代表没有)。
为了解决这个问题,我们可以尝试将该合唱团的全部团员放在队尾。
此时,我们需要将不在 到
中的队员移进来(len为处理完成的人数,该区间就是此团队在排列完的队列中所在的位置)
需要的代价为 ,这里的
代表前
个位置中属于团队
的团员个数,我们可以在初始化中完成这个操作。
最后是damn代码:
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int maxn=21,maxm=100005;
int n,m;
int f[1<<maxn],num[maxn],sum[maxm][maxn];
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
int x;
cin>>x;
num[x]++;//每个乐队的成员数
for(int j=1;j<=m;j++)sum[i][j]=sum[i-1][j];
sum[i][x]++;//前i人中属于团队j的成员人数
}
memset(f,0x3f,sizeof(f));
f[0]=0;//没有偶像出列
for(int i=0;i<(1<<m);i++){
int len=0;//已安排位置人数
for(int j=1;j<=m;j++){
if(i&(1<<(j-1)))len+=num[j];
}
for(int j=1;j<=m;j++){
if(i&(1<<(j-1)))f[i]=min(f[i],f[i^(1<<(j-1))]+num[j]-sum[len][j]+sum[len-num[j]][j]);
}
}
cout<<f[(1<<m)-1]<<endl;//状态为2^m-1时的最优解
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】