邦邦的大合唱站隊
阿新 • • 發佈:2018-11-08
題目
https://www.luogu.org/problemnew/show/P3694
思路
狀壓dp
dp【i】表示達到i狀態出隊的最小人數,sum【i】【j】表示前i個人有幾個屬於j樂隊那麼列舉一個l,r則有dp[i|(1<<j)]=min(dp[i|(1<<j)],dp[i]+(r-l-(sum[r][j]-sum[l][j])));
程式碼
#include<iostream> #include<cstdio> #include<cstring> const int inf=1e9,maxn=1e5+5,maxm=21; using namespace std; int n,m,a[maxn],dp[(1<<maxm)+1],sum[maxn][maxm]; 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]); }