POJ 2436
思路:需要用到位运算,D最为16,用一个int数(32位 > 16位,足够表示)的每个二进制位表示病毒的存在与否,1为存在,0不存在,这样复杂度为2^16*n(通过剪枝实际达不到这么大),可以接受。因此有两种方法解本题,(1),dfs,枚举D的k-组合。(2),通过二进制枚举D的k-组合。用dfs,最后程序跑得时间是32ms,二进制枚举跑得时间是285ms,如此大的差距,原因就是二进制把所有组合都枚举完了,其中包含很多冗余状态,说白了就是大爆力。
DFS版(32ms):
#include<iostream> #include<cstring> #include<cstdio> #include<cmath> #include<string> #include<algorithm> #define MAXN 1111 using namespace std; int cow[MAXN],vis[MAXN],N,D,K,ans; void dfs(int idx,int cnt,int sum){ if(cnt == K){ int num = 0; for(int i = 0;i < N;i ++) if(cow[i] == (cow[i] & sum)) num++; ans = max(ans,num); return; } for(int i = idx;i < D;i ++){ if(!vis[i]){ vis[i] = 1; dfs(i+1,cnt+1,sum|(1 << i)); vis[i] = 0; } } } int main(){ int tmp,kind; // freopen("in.c","r",stdin); while(~scanf("%d%d%d",&N,&D,&K)){ memset(cow,0,sizeof(cow)); memset(vis,0,sizeof(vis)); for(int i = 0;i < N;i ++){ scanf("%d",&tmp); for(int j = 0;j < tmp;j ++){ scanf("%d",&kind); cow[i] |= (1 << (kind-1)); } } ans = 0; dfs(0,0,0); printf("%d\n",ans); } return 0; }
2.二进制枚举版(285ms):
#include<iostream> #include<cstring> #include<cstdio> #include<cmath> #include<string> #include<algorithm> #define MAXN 1111 using namespace std; int cow[MAXN]; int count_digit(int n){ int cnt = 0; for(int i = 0;i < 32;i ++) if(n & (1 << i)) cnt ++; return cnt; } int main(){ int n,m,k,tmp,kind; //freopen("in.c","r",stdin); while(~scanf("%d%d%d",&n,&m,&k)){ memset(cow,0,sizeof(cow)); for(int i = 0;i < n;i ++){ scanf("%d",&tmp); for(int j = 0;j < tmp;j ++){ scanf("%d",&kind); cow[i] |= (1 << (kind-1)); } } int ans = 0; for(int i = 0;i < (1 << m);i ++){ int sum = 0,cnt = 0; if(count_digit(i) > k) continue; for(int j = 0;j < n;j ++) if((cow[j] | i) == i) cnt++; ans = max(ans,cnt); } printf("%d\n",ans); } return 0; }