1. 程式人生 > >P3694 邦邦的大合唱站隊/簽到題(狀壓dp)

P3694 邦邦的大合唱站隊/簽到題(狀壓dp)

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??(1a?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=2N20,M=2

對於40%的數據,N\le 100, M\le 4N100,M4

對於70%的數據,N\le 2000, M\le 10N2000,M10

/*
狀壓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 201N10?5??,M20

P3694 邦邦的大合唱站隊/簽到題(狀壓dp)