题目链接:
code:
//Must so #include<iostream> #include<cstring> #include<cstdio> #include<vector> #define mem(a,x) memset(a,x,sizeof(a)) #define inf (1<<29) using namespace std; typedef long long ll; /* 读题就读了半天=-= N台电脑,每台有N种服务,同时这N台电脑构成网络(输入就是电脑的关系) 黑客要做的事是,对于每台电脑,选择1种服务去攻击 当攻击电脑x的y服务,和x直接相连的电脑的y服务都会停止 当一种服务在所有N台电脑都是"停止"状态的时候,我们称这种服务"崩溃" now ,你要做的是,对于每台电脑,选择一种服务去攻击,使得处于"崩溃"状态的服务最多 给的N最大16,显然是要状态压缩的 这样,对于服务i,i在编号0~n-1的n台电脑上被攻击记为1,不被攻击记为0 state[i]记录了满足条件的状态 所谓满足条件是说:例如001100表示在2、3号电脑上攻击可以做到使所有电脑都被攻击 我是转化成01背包的思想=-= 然后把这些满足条件的状态尽可能多的"装"起来 每种状态是一个物品,物品的体积是状态二进制里面的"1",物品的价值的1 装的时候要满足选择装进背包的状态不能有交集 */ const int N = 1<<16; vector<int>s[20]; int state[N+5]; int dp[N+5]; int n; int tot;//记录满足条件的状态总数 bool ok(int x)//判断状态x是否满足条件 { bool vis[20] = {0}; for (int i = 0;i < n;++i) { if ((x>>i)&1)//选择攻击第i台电脑 { vis[i] = 1; for (int j = 0;j < s[i].size();++j) { vis[s[i][j]] = 1;//和该台被攻击的电脑直接相连的电脑的服务也停止 } } } for (int i = 0;i < n;++i)//判断是否每台电脑都被攻击到了 { if (!vis[i]) return 0; } return 1; } /* 剪枝:比如两种状态,000111和000001, 如果第二种状态就ok,当然没必要要第一种状态=-= */ vector<int>q; void init() { tot = 0; q.clear(); int v = (1<<n)-1; for (int i = 1;i <= v;++i) { dp[i] = 0; bool yes = 0; for (int j = 0;j < tot;++j) { if ((i&q[j]) == q[j]) { yes = 1; break; } } if (yes) continue; if (ok(i)) { q.push_back(i); state[tot++] = i; } } } int main() { int kas = 0; while (~scanf("%d",&n)) { if (n==0) break; for (int i = 0;i < n;++i) { int m;scanf("%d",&m); s[i].clear();//初始化=-= for (int j = 0,x;j < m;++j) { scanf("%d",&x); s[i].push_back(x); } } init(); int V = (1<<n)-1;//背包体积=-= for (int i = 0;i < tot;++i) { for (int j = V;j >= 0;--j) { if ((j&state[i]) == 0)//保证不能有交集=-= { dp[j|state[i]] = max(dp[j|state[i]] ,dp[j] + 1); }//二进制的|运算抽象成"加" } } printf("Case %d: %d\n",++kas,dp[V]); } return 0; }