P3694 邦邦的大合唱站隊/簽到題(狀壓dp)
阿新 • • 發佈:2017-09-24
pre fontsize print nts scrip 安排 裏的 ret std
P3694 邦邦的大合唱站隊/簽到題
題目背景
BanG Dream!裏的所有偶像樂隊要一起大合唱,不過在排隊上出了一些問題。
題目描述
N個偶像排成一列,他們來自M個不同的樂隊。每個團隊至少有一個偶像。
現在要求重新安排隊列,使來自同一樂隊的偶像連續的站在一起。重新安排的辦法是,讓若幹偶像出列(剩下的偶像不動),然後讓出列的偶像一個個歸隊到原來的空位,歸隊的位置任意。
請問最少讓多少偶像出列?
輸入輸出格式
輸入格式:
第一行2個整數N,M。
接下來N個行,每行一個整數a_i(1\le a_i \le M)a?i??(1≤a?i??≤M),表示隊列中第i個偶像的團隊編號。
輸出格式:
一個整數,表示答案
輸入輸出樣例
輸入樣例#1:12 4 1 3 2 4 2 1 2 3 1 1 3 4輸出樣例#1:
7
說明
【樣例解釋】
1 3 √
3 3
2 3 √
4 4
2 4 √
1 2 √
2 2
3 2 √
1 1
1 1
3 1 √
4 1 √
【數據規模】
對於20%的數據,N\le 20, M=2N≤20,M=2
對於40%的數據,N\le 100, M\le 4N≤100,M≤4
對於70%的數據,N\le 2000, M\le 10N≤2000,M≤10
/* 狀壓dp 狀態:dp[i]表示i狀態下最小的出列(不一致)的個數。 比如dp[1101]表示從頭到位為1/3/4樂隊的偶像的最小出列個數。 預處理sum[i][j]表示前i個人中j種的數量 dp[i|(1<<j)]=min(dp[i|(1<<j)],dp[i]+(r-l-(sum[r][j]-sum[l][j]))); */ #include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #define inf 100000000 #define N 100007 using namespace std; int n,m; int a[N],dp[(1<<21)+1],sum[N][21]; int main() { scanf("%d%d",&n,&m); for(int i=1; i<=n; i++) { scanf("%d",&a[i]); a[i]--; for(int j=0; j<m; j++) { sum[i][j]=sum[i-1][j]; if(j==a[i]) sum[i][j]++; } } for(int i=0; i<(1<<m); i++) dp[i]=inf; dp[0]=0; for(int i=0; i<(1<<m); i++) { int Sum=0; for(int j=0; j<m; j++) if((1<<j)&i) Sum+=sum[n][j]; for(int j=0; j<m; j++) { if((1<<j)&i) continue; int num=sum[n][j]; int r=Sum+num; int l=Sum; dp[i|(1<<j)]=min(dp[i|(1<<j)],dp[i]+(r-l-(sum[r][j]-sum[l][j]))); } } printf("%d\n",dp[(1<<m)-1]); return 0; }
對於全部數據,1\le N\le 10^5, M\le 201≤N≤10?5??,M≤20
P3694 邦邦的大合唱站隊/簽到題(狀壓dp)