狀壓DP 【洛谷P3694】 邦邦的大合唱站隊
阿新 • • 發佈:2018-10-13
是個 ret ace 輸出 con 重新 tdi char getchar
pos就是當前不算第i個團隊的人的最後位置,枚舉一邊就可以了。
【洛谷P3694】 邦邦的大合唱站隊
題目背景
BanG Dream!裏的所有偶像樂隊要一起大合唱,不過在排隊上出了一些問題。
題目描述
N個偶像排成一列,他們來自M個不同的樂隊。每個團隊至少有一個偶像。
現在要求重新安排隊列,使來自同一樂隊的偶像連續的站在一起。重新安排的辦法是,讓若幹偶像出列(剩下的偶像不動),然後讓出列的偶像一個個歸隊到原來的空位,歸隊的位置任意。
請問最少讓多少偶像出列?
輸入輸出格式
輸入格式:
第一行2個整數N,M。
接下來N個行,每行一個整數\(a_i (1\le a_i \le M)\),表示隊列中第i個偶像的團隊編號。
輸出格式:
一個整數,表示答案
一開始看這個題真的沒有思路,想了一下直接寫了個爆搜,枚舉每個團隊的開頭位置在求答案取最小。
復雜度\(O(n*m!)\)不過真的意外可以過70分。
暴搜
code:
#include<iostream> #include<cstdio> using namespace std; const int wx=201007; int tot[wx],sum[wx][17],vis[wx],pre[wx],last[wx],E[wx]; inline int read(){ int sum=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){sum=(sum<<1)+(sum<<3)+ch-'0';ch=getchar();} return sum*f; } int n,m,x; int ans=0x3f3f3f3f; void dfs(int now,int end,int num){ if(now==m+1&&end==n){ ans=min(ans,num); return ; } if(num>=ans)return; for(int i=1;i<=m;i++){ if(vis[i])continue; vis[i]=1; dfs(now+1,end+tot[i],tot[i]-sum[end+tot[i]][i]+sum[end][i]+num); vis[i]=0; } } signed main(){ n=read();m=read(); for(int i=1;i<=n;i++){ x=read(); for(int j=1;j<=m;j++)sum[i][j]=sum[i-1][j]; sum[i][x]=sum[i-1][x]+1; tot[x]++; } dfs(1,0,0); printf("%d\n",ans); return 0; }
正解知道是狀壓,但是連狀態都不會設(菜死了菜死了。。。)
看了大佬博客才發現這題設了狀態就完事了。。。
設\(f(i)\)表示當前狀態下的最優答案。因為數據範圍,肯定是要狀壓m,怎麽壓是個問題。
其實我們用二進制的一位表示一個團隊,那麽1代表這個團隊已經站好了,0表示還沒有站好。
這裏的站好定義要明確,就是這個團隊裏的每個人都緊挨著了,並且我們默認這些團隊都是從頭開始向後緊挨著的,(每一次取min保證之前的狀態站法是最優的)。
轉移就比較好想了。
\[
f(i)=f(i\ ^ \ (1<<j-1))+tot(j)-(sum(pos+tot(j))(j)-sum(pos)(j))
\]
pos就是當前不算第i個團隊的人的最後位置,枚舉一邊就可以了。
復雜度\(O(m*m!)\)
DP
code:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int wx=100017;
inline int read(){
int sum=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){sum=(sum<<1)+(sum<<3)+ch-'0';ch=getchar();}
return sum*f;
}
int n,m;
int f[1<<20],sum[wx][21],tot[21];
int main(){
n=read();m=read();
for(int i=1;i<=n;i++){
int x;
x=read();
for(int j=1;j<=m;j++)sum[i][j]=sum[i-1][j];
sum[i][x]=sum[i][x]+1;
tot[x]++;
}
memset(f,0x3f,sizeof f);
f[0]=0;int WX=(1<<m);
for(int i=1;i<WX;i++){
int pos=0;
for(int j=1;j<=m;j++){
if(i&(1<<j-1)){
pos+=tot[j];
}
}
for(int j=1;j<=m;j++){
if(i&(1<<j-1)){
f[i]=min(f[i],f[i^(1<<j-1)]+tot[j]+sum[pos-tot[j]][j]-sum[pos][j]);
}
}
}
printf("%d\n",f[WX-1]);
return 0;
}
狀壓DP 【洛谷P3694】 邦邦的大合唱站隊