题目链接:https://vjudge.net/contest/370423#problem/B
题意有点难理解,也就是给出N个结点,每个结点关联它自身和一些其他的点,每当所有结点都被破坏时,相当于破坏了一个服务。询问最多可以破坏多少组服务。
解题思路:
①p[i]表示第i个结点关联的点,用二进制表示
for(int i=0;i<n;i++){
int cnt;
scanf("%d",&cnt);
p[i]=1<<i;
for(int j=0;j<cnt;j++){
int temp;
scanf("%d",&temp);
p[i]|=1<<temp;
}
}
②然后二进制枚举所有情况,令len=(1<<(n+1))-1种情况(这里也可以从0位开始,一共有1<<n-1种情况),然后利用c[i]保存一个状态可以破坏的结点情况
for(int i=1;i<=len;i++){
for(int j=0;j<n;j++){
if(i&(1<<j))
c[i]|=p[j];
}
}
③最后用dp来遍历二进制所有情况,以及情况对应的子集
第一个i循环是遍历所有的二进制情况
第二个j循环,是遍历i情况的所有子集,来保持动态,更新状态
条件是这个子集满足能够破坏所有结点
for(int i=1;i<=len;i++){
for(int j=i;j;j=(j-1)&i){
if(c[j]==len) //如果j情况可以破坏所有的结点,则算作一次
dp[i]=max(dp[i],dp[i^j]+1);
}
}
④最终结果一定是破坏所有情况下即1111111…时能够破坏的服务器数
#include<iostream>
#include<cstdio>
#include<string.h>
#include<string>
#include<vector>
using namespace std;
const int maxn=1<<16;
int n;
int p[maxn];
int c[maxn];
int dp[maxn];
int main()
{
int t=0;
while(scanf("%d",&n)!=EOF){
t++;
if(n==0) break;
memset(dp,0,sizeof(dp));
memset(p,0,sizeof(p));
memset(c,0,sizeof(c));
for(int i=0;i<n;i++){
int cnt;
scanf("%d",&cnt);
p[i]=1<<i;
for(int j=0;j<cnt;j++){
int temp;
scanf("%d",&temp);
p[i]|=1<<temp;
}
}
int len=(1<<n)-1;
for(int i=1;i<=len;i++){
for(int j=0;j<n;j++){
if(i&(1<<j))
c[i]|=p[j];
}
}
for(int i=1;i<=len;i++){
for(int j=i;j;j=(j-1)&i){
if(c[j]==len)
dp[i]=max(dp[i],dp[i^j]+1);
}
}
printf("Case %d: %d\n",t,dp[len]);
}
return 0;
}