hdu5305 Friends(dfs+map/hash)
题目:http://acm.hdu.edu.cn/showproblem.php?pid=5305
题意:给定N个人和M条朋友关系,是朋友关系的两个人之间有两种联系方式online和offline。使每一个人的online的数量和offline的数量相等,求方案数。
分析:因为M<=28,暴力枚举的话2^28非常大,会超时。能够考虑把全部的状态平分成两半,即枚举前面M/2条关系,暴力求出前面的2^(M/2)种状态,然后枚举后面M/2条关系,暴力求出后面2^(M/2)种状态,再枚举后面一半的状态,对于每一种状态直接在前面一半的状态里面找出满足条件的就可以。找状态用dfs比二进制枚举要快,查询的话map比hash方便。。。
代码:
#include <iostream> #include <vector> #include <cstdio> #include <cstring> #include <map> using namespace std; int N,M,cnt[20],buf[20],f[20],ans,tp; struct node { int a,b; }e[200]; map <vector<int>,int> mp; void dfs(int x,int y) { if(x>y) { if(tp==1) mp[vector <int> (buf+1,buf+N+1)]++; else { for(int i=1;i<=N;i++) f[i]=(cnt[i]>>1)-buf[i]; if(mp.find(vector <int> (f+1,f+N+1))!=mp.end()) ans+=mp[vector <int> (f+1,f+N+1)]; } return ; } buf[e[x].a]++; buf[e[x].b]++; if(buf[e[x].a]<=(cnt[e[x].a]>>1) && buf[e[x].b]<=(cnt[e[x].b]>>1)) dfs(x+1,y); buf[e[x].a]--; buf[e[x].b]--; dfs(x+1,y); } int main() { int ncase,i,j,z; scanf("%d",&ncase); while(ncase--) { scanf("%d%d",&N,&M); mp.clear(); memset(cnt,0,sizeof(cnt)); ans=0; for(i=1;i<=M;i++) { scanf("%d%d",&e[i].a,&e[i].b); cnt[e[i].a]++; cnt[e[i].b]++; } z=1; for(i=1;i<=N;i++) if(cnt[i]&1) z=0; if(!z) { printf("0\n"); continue ; } tp=1; dfs(1,M/2); tp=2; dfs(M/2+1,M); printf("%d\n",ans); } return 0; }
另一种更简单的方法,直接dfs然后剪一下枝。假设一个人有x个朋友,那么暴力搜的过程中出现online的个数>x/2或者offline的个数>x/2就不满足条件也就不用搜了。
#include <iostream> #include <cstdio> using namespace std; int on[10],off[10],d[10],N,M,ans,x[30],y[30]; void dfs(int cur) { if(cur>M) { for(int i=1;i<=N;i++) if(on[i]!=off[i]) return ; ++ans; return ; } int u=x[cur],v=y[cur]; if(on[u]<d[u]/2 && on[v]<d[v]/2) { on[u]++;on[v]++; dfs(cur+1); on[u]--;on[v]--; } if(off[u]<d[u]/2 && off[v]<d[v]/2) { off[u]++;off[v]++; dfs(cur+1); off[u]--;off[v]--; } } int main() { int ncase,i,j,k; scanf("%d",&ncase); while(ncase--) { scanf("%d%d",&N,&M); memset(d,0,sizeof(d)); for(i=1;i<=M;i++) { scanf("%d%d",&x[i],&y[i]); d[x[i]]++; d[y[i]]++; } ans=0; dfs(1); printf("%d\n",ans); } return 0; }