洛谷P3694 邦邦的大合唱站队/签到题

P3694 邦邦的大合唱站队/签到题

题目背景

BanG Dream!里的所有偶像乐队要一起大合唱,不过在排队上出了一些问题。

题目描述

N个偶像排成一列,他们来自M个不同的乐队。每个团队至少有一个偶像。

现在要求重新安排队列,使来自同一乐队的偶像连续的站在一起。重新安排的办法是,让若干偶像出列(剩下的偶像不动),然后让出列的偶像一个个归队到原来的空位,归队的位置任意。

请问最少让多少偶像出列?

输入输出格式

输入格式:

第一行2个整数N,M。

接下来N个行,每行一个整数a_i(1le a_i le M)ai(1aiM),表示队列中第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%的数据,Nle 20, M=2N20,M=2

对于40%的数据,Nle 100, Mle 4N100,M4

对于70%的数据,Nle 2000, Mle 10N2000,M10

对于全部数据,1le Nle 10^5, Mle 201N105,M20

#include<cstdio>
const int N=100005,M=26;
int a[N],b[M],n,m,s[M],sum[M][N],ans=1<<30;
bool vis[M];
void dfs(int i, int tot){
    if(i>n){
        if(tot<ans) ans=tot;
        return;
    }
    if(tot>ans) return;
    for(int j=1;j<=m;j++)
        if(!vis[j]){
            vis[j]=true;
            dfs(i+s[j],tot+s[j]-sum[j][i+s[j]-1]+sum[j][i-1]);
            vis[j]=false;
        }
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        s[a[i]]++;
    }
    for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++){
            sum[i][j]=sum[i][j-1];
            if(a[j]==i) sum[i][j]++;
        } 
    dfs(1,0);
    printf("%d",ans);
}
70分 暴力
/*
    状态压缩,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>
using namespace std;
int n,m,dp[(1<<20)+1],a[100010],sum[100010][21];
int main(){
    freopen("Cola.txt","r",stdin);
    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(a[i]==j)sum[i][j]++;
        }
    }
    for(int i=0;i<(1<<m);i++)dp[i]=0x7fffffff;dp[0]=0;
    for(int i=0;i<(1<<m);i++){
        int s=0;
        for(int j=0;j<m;j++)if(i&(1<<j))s+=sum[n][j];
        for(int j=0;j<m;j++){
            if(i&(1<<j))continue;
            int num=sum[n][j];
            int r=s+num,l=s;
            dp[i|(1<<j)]=min(dp[i|(1<<j)],(r-l-(sum[r][j]-sum[l][j])+dp[i]));
        }
    }
    printf("%d",dp[(1<<m)-1]);
}
100分 状压dp
原文地址:https://www.cnblogs.com/thmyl/p/7710800.html