1. 程式人生 > >邦邦的大合唱站隊

邦邦的大合唱站隊

題目

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]);
}