zoukankan      html  css  js  c++  java
  • [Luogu] P3694 邦邦的大合唱站队

    (Link)

    Description

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

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

    请问最少让多少偶像出列?((1le{N}le10^5,Mle20))

    Solution

    注意到全排列是过不去的,考虑状压。

    (dp[i])表示(i)这个状态(二进制)下为(1)的乐队都排好位置时最小的出队人数。转移就是由(i)中把一个乐队排到最后来转移。然后用前缀和来快速判断一段区间内某一乐队的偶像数量。

    Code

    #include <bits/stdc++.h>
    
    using namespace std;
    
    int n, m, a[100005], dp[1050005], sum[100005][25], tot[25];
    
    int read()
    {
    	int x = 0, fl = 1; char ch = getchar();
    	while (ch < '0' || ch > '9') { if (ch == '-') fl = -1; ch = getchar();}
    	while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + ch - '0'; ch = getchar();}
    	return x * fl;
    }
    
    int main()
    {
    	n = read(); m = read();
    	memset(dp, 0x3f, sizeof(dp));
    	dp[0] = 0;
    	for (int i = 1; i <= n; i ++ )
    	{
    		a[i] = read();
    		tot[a[i]] ++ ;
    		for (int j = 1; j <= m; j ++ )
    			sum[i][j] = sum[i - 1][j];
    		sum[i][a[i]] ++ ;
    	}
    	for (int i = 0; i <= (1 << m) - 1; i ++ )
    	{
    		int cnt = 0;
    		for (int j = 1; j <= m; j ++ )
    			if (i & (1 << (j - 1)))
    				cnt += tot[j];
    		for (int j = 1; j <= m; j ++ )
    			if (i & (1 << (j - 1)))
    				dp[i] = min(dp[i], dp[i - (1 << (j - 1))] + sum[n][j] - sum[cnt][j] + sum[cnt - tot[j]][j]);
    	}
    	printf("%d
    ", dp[(1 << m) - 1]);
    	return 0;
    }
    
  • 相关阅读:
    hdu 6702 ^&^ 位运算
    hdu 6709 Fishing Master 贪心
    hdu 6704 K-th occurrence 二分 ST表 后缀数组 主席树
    hdu 1423 Greatest Common Increasing Subsequence 最长公共上升子序列 LCIS
    hdu 5909 Tree Cutting FWT
    luogu P1588 丢失的牛 宽搜
    luogu P1003 铺地毯
    luogu P1104 生日
    luogu P1094 纪念品分组
    luogu P1093 奖学金
  • 原文地址:https://www.cnblogs.com/andysj/p/14025400.html
Copyright © 2011-2022 走看看