题意是将n个集合P1,P2,P3,……,Pn分成尽量多组,使得每组中所有集合的并集等于全集。
Pi是对于计算机i及其相邻计算机的集合。
每个组就相当于一项服务。
整个动规相当于对一个集合进行划分。
对于一个集合S,我们枚举它的子集S0,如果S0已经满足了要求,我们就可以把S0从S中划去,再去研究剩下的部分。
划去的操作是S^S0
这里注意一下枚举一个集合的子集的操作,非常巧妙:
S0 = (S0 - 1)& S
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn = 16; int n; int p[maxn], cover[1 << maxn], f[1 << maxn]; int main() { int kase = 0; while (scanf("%d", &n) && n) { kase++; memset(p, 0, sizeof p); for (int i = 0; i < n; i++) { p[i] = 1 << i; int num; scanf("%d", &num); for (int j = 1; j <= num; j++) { int x; scanf("%d", &x); p[i] |= (1 << x); } } for (int S = 0; S < (1 << n); S++) { cover[S] = 0; for (int i = 0; i < n; i++) if (S & (1 << i)) cover[S] |= p[i]; } f[0] = 0; int ALL = (1 << n) - 1; for (int S = 1; S < (1 << n); S++) { f[S] = 0; for (int S0 = S; S0; S0 = (S0 - 1) & S)//枚举子集 if (cover[S0] == ALL) f[S] = max(f[S], f[S ^ S0] + 1); } printf("Case %d: %d ", kase, f[ALL]); } return 0; }