P3694 邦邦的大合唱站队
作者:互联网
看范围,N 较大, M 符合状压的范围。
f[S] 表示 编号为L(L是S的一位的团队)且这一位为1 的团队已经摆好了.例如1011就是将1,2,4团队的歌手都摆好的最小值。
我们考虑如何转移。在Dp转移中,我们都是通过决策,将未知的状态由已知的状态推出,那么决策显然就是将一个团队的歌手摆好需要多摆几个人
假设现在状态集合为S,那么少一个歌手团队的集合就是S ^ (1 << j-1) ,这个集合我们设为K,那么f[S] 就由 f[K]转移。
我们就让K集合比S集合少的人数累计到答案里。特别显然的是,肯定不需要所有人都离开位置,以前就在那个位置上的可以不移动,所以在减去原先就在那个范围的人。
我们需要预处理很多东西
1.我们需要预处理sum[i]表示i团队一共有多少人,预处理简单
2.我们需要预处理1 - j的这些位置i团队有多少人,q[i][j]
3.我们需要预处理集合S中,某些位是1的歌手的和
完整的方程就是
f[S] = f[K] + sum[j] -(q[j][s[i]] - q[j][s[K]]);
K = S ^ (1 << j-1)
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N = 1e5 + 10; const int M = 22; int f[1 << M]; int n, m; int a[N], q[M][N]; //前缀和,q[M][N];q[i][j] 1 - j 有多少个i团队的人 //The number of singers who work in the team i that singing in (1 to j) int sum[M]; int S[1 << M]; int main(){ cin >> n >> m; for(int i = 1;i <= n;++ i) cin >> a[i]; for(int i = 1;i <= n;++ i){ sum[a[i]] ++; q[a[i]][i] = 1; } for(int i = 1;i <= n;++ i){ for(int j = 1;j <= m;++ j) q[j][i] += q[j][i-1]; } for(int i = 1;i < 1 << m;++ i){//O(2^m * m) int k = 0; int tmp = i; while(tmp){ ++ k; if(tmp & 1) S[i] += sum[k]; tmp >>= 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 k = i ^ (1 << (j - 1)); f[i] = min(f[i], f[k] + sum[j] - (q[j][S[i]] - q[j][S[k]])); } } cout << f[(1<<m)-1]; return 0; }
标签:include,大合唱,int,歌手,P3694,站队,集合,团队,预处理 来源: https://www.cnblogs.com/Shu-Kuang/p/13325037.html