P3694 邦邦的大合唱站队 题解

🔗


\( 数据范围暗示状压,爪巴。 \\ 首先考虑状态量。 \\ 我们设计一个关于乐队数量的状态 S, 代表排列好的乐队。\\ \)
eg:

if(Set_排列好的队列 = {1, 2, 5}) 
    then  S = 010011

\( 设f[S]为S状态下排列好的最小代价 \\ s[i][j]为前i个位置有多少个j乐队成员 \\ num[j] 乐队j的人数\\ p.s. 以上三者都可以预处理\\ 然后我们就可以得出一个结论: 对于第j个乐队 \)

\[f[S]=min(f[ S \ xor \ (1<<j) ]+num[j]−s[r][j]+s[l][j] \]

\(其中num[j] - s[r][j] + s[l][j]是乐队j的花费\)
这么说来,倒是有一点背包的味道了。


#include <bits/stdc++.h>
#define LL long long
#define il inline
#define rg register
using namespace std;
int t, n, m;
const int maxn = 2e6 + 5; 
const int maxs = 2e5 + 5;
il void chkmax(int &a, int b) {a = a > b ? a : b;}
il void chkmin(int &a, int b) {a = a < b ? a : b;}
il int read() {
	int x = 0, f = 1; char c = getchar();
	while(!isdigit(c)) {
		if(c == '-') f = -f;
		c = getchar();
	}
	while(isdigit(c)) {
		x = (x << 1) + (x << 3) + c - '0';
		c = getchar();
	}
	return x * f;
}
il void write(int x) {
	char c[33] = {0}, tot = 0;
	if(x == 0) {puts("0"); return;}
	while(x) {c[++ tot] = x % 10 + '0'; x /= 10;}
	while(tot) {putchar(c[tot --]);}
	return ;
}

int f[maxn]; 
int s[maxs][30], num[30], sum[maxn];
il bool chk(int state, int d) {
	return state & (1 << d - 1);
}
il void dfs(int x, int s, int d) {
	if(x ^ m) {
		if(d == 1) {
			sum[s | (1 << x)] = sum[s] + num[x + 1];
			dfs(x + 1, (s | (1 << x)), 1);
			dfs(x + 1, (s | (1 << x)), 0); 
		} else {
			dfs(x + 1, s, 1);
			dfs(x + 1, s, 0);
		}
	}
}
int main() {
	n = read(), m = read();
	for(int i = 1, x;i <= n;i ++) {
		x = read(); 
		for(int j = 1;j <= m;j ++) {
			s[i][j] = s[i - 1][j];
		}
		s[i][x] ++, num[x] ++;
	}
	dfs(0, 0, 0); dfs(0, 0, 1);
	memset(f, 0x3f, sizeof(f));
	f[0] = 0;
	for(int i = 1;i < (1 << m);i ++) {
		for(int j = 1;j <= m;j ++) {
			int l = sum[i ^ (1 << j - 1)];
			int r = sum[i];
			if(chk(i, j)){
				chkmin(f[i], f[i ^ (1 << j - 1)] + (r - l) - (s[r][j] - s[l][j]));
			}
		}
	}
	cout << f[(1 << m) - 1];
	return 0;
}
posted @ 2020-07-12 22:02  永远_少年  阅读(155)  评论(0编辑  收藏  举报