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;
    }
    
  • 相关阅读:
    poj2386 Lake Counting
    poj 1852 Ants
    Luogu P2419 [USACO08JAN]牛大赛Cow Contest
    Luogu P2336 [SCOI2012]喵星球上的点名
    Luogu P2463 [SDOI2008]Sandy的卡片
    Luogu P2852 [USACO06DEC]牛奶模式Milk Patterns
    Luogu P4248 [AHOI2013]差异
    【NOI2008】志愿者招募
    Luogu P2743 [USACO5.1]乐曲主题Musical Themes
    P3723 [AH2017/HNOI2017]礼物
  • 原文地址:https://www.cnblogs.com/andysj/p/14025400.html
Copyright © 2011-2022 走看看