uva11825

题意是将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;
}
原文地址:https://www.cnblogs.com/yohanlong/p/7762506.html