哇。。终于给写出来了。。
题意很简单:
把n个人分成2各组,每一个人有他所认识的人。所分的组有四点要求:
1、 每个人都必需属于一个组。
2、 每个组至少有一个人。
3、 每个组里面的每个人必需互相认识。
4、 两个组的成员应尽量接近。
刚开始的时候以为只有一个连通分量呢,没想那么多,一直wa,后来发现每个人都用dp,感觉很纳闷,最后想想才发现,有可能有很多个连通分量。。。
感觉用并查集处理起来会比较麻烦,就改用广搜了,然后记录每一个点层次
如果是第k个连通分量的,奇数层次上的点存在s1[k][]里面,s1[k][0]记录点的个数,偶数层次上的放在s2[k][]里面,s2[k][0]记录点的个数。。
判断一下s1[k]里面的任意两个点是否都能认识, s2[k]里面的任意两个点是否都能认识, 如果存在不认识的,就直接返回No solution
最后用背包背下就行了,dp初始化很重要,dp[0][0]=1,其他的全部赋为0,
代码:
View Code
1 # include<stdio.h> 2 # include<string.h> 3 # include<queue> 4 using namespace std; 5 int map[105][105],dp[105][105],adj[105][105],visit[105],n,m,s1[105][105],s2[105][105],pre1[105]; 6 struct node{ 7 int xuhao,val; 8 }; 9 void dfs(int i)/*纯广搜*/ 10 { 11 int j,ans; 12 queue<node>q; 13 node cur,next; 14 cur.xuhao=i; 15 cur.val=1;/*顶点的层次赋为1*/ 16 q.push(cur); 17 visit[i]=1; 18 m++; 19 while(!q.empty()) 20 { 21 cur=q.front(); 22 q.pop(); 23 ans=cur.xuhao; 24 if(cur.val%2==1) {s1[m][0]++;s1[m][s1[m][0]]=ans;} 25 else {s2[m][0]++;s2[m][s2[m][0]]=ans;} 26 for(j=1;j<=n;j++) 27 { 28 if(j!=ans && visit[j]==0 && adj[ans][j]==1) 29 { 30 next.xuhao=j; 31 next.val=cur.val+1; 32 visit[j]=1; 33 q.push(next); 34 } 35 } 36 } 37 } 38 int main() 39 { 40 int i,j,t,h,k,ncase,k1,flag,x; 41 scanf("%d",&t); 42 for(ncase=1;ncase<=t;ncase++) 43 { 44 if(ncase!=1) printf("\n"); 45 scanf("%d",&n); 46 memset(map,0,sizeof(map)); 47 for(i=1;i<=n;i++) 48 { 49 while(scanf("%d",&x)!=EOF &&x) 50 map[i][x]=1; 51 } 52 memset(adj,0,sizeof(adj)); 53 for(i=1;i<=n;i++) 54 { 55 for(j=i+1;j<=n;j++) 56 if(map[i][j]==0 || map[j][i]==0) {adj[i][j]=1;adj[j][i]=1;}/*把不认识的连在起来*/ 57 } 58 memset(visit,0,sizeof(visit)); 59 for(i=1;i<=n+1;i++) 60 { 61 s1[i][0]=0; 62 s2[i][0]=0; 63 }/*初始化每一个连通分量*/ 64 m=0;/*表示连通分量的个数*/ 65 flag=0; 66 for(i=1;i<=n;i++) 67 { 68 if(visit[i]==0)/*如果该点没有被访问过*/ 69 { 70 dfs(i); 71 for(j=1;j<=s1[m][0];j++) 72 { 73 for(k=j+1;k<=s1[m][0];k++) 74 if(adj[s1[m][j]][s1[m][k]]==1) {flag=1;break;} 75 if(flag==1) break; 76 } 77 for(j=1;j<=s2[m][0];j++) 78 { 79 for(k=j+1;k<=s2[m][0];k++) 80 if(adj[s2[m][j]][s2[m][k]]==1) {flag=1;break;} 81 }/*判断一下每一个连通分量里面的点是否都能认识*/ 82 } 83 if(flag==1) break; 84 } 85 if(flag==1) 86 { 87 printf("No solution\n"); 88 continue; 89 } 90 memset(dp,0,sizeof(dp)); 91 dp[0][0]=1; 92 for(i=1;i<=m;i++) 93 { 94 for(j=n/2;j>=0;j--) 95 if(dp[i-1][j]) 96 { 97 dp[i][j+s1[i][0]]=1; 98 dp[i][j+s2[i][0]]=2; 99 } 100 } 101 for(j=n/2;j>=0;j--) 102 { 103 if(dp[m][j]) break; 104 }/*找点数和离n/2最近的一个组合*/ 105 k1=0; 106 for(i=m;i>=1;i--) 107 { 108 if(dp[i][j]==1) 109 { 110 j-=s1[i][0]; 111 for(h=1;h<=s1[i][0];h++) 112 pre1[++k1]=s1[i][h]; 113 } 114 else 115 { 116 j-=s2[i][0]; 117 for(h=1;h<=s2[i][0];h++) 118 pre1[++k1]=s2[i][h]; 119 } 120 } 121 memset(visit,0,sizeof(visit)); 122 printf("%d",k1); 123 for(i=1;i<=k1;i++) 124 { 125 printf(" %d",pre1[i]); 126 visit[pre1[i]]=1; 127 } 128 printf("\n"); 129 printf("%d",n-k1); 130 for(i=1;i<=n;i++) 131 { 132 if(visit[i]==0) 133 printf(" %d",i); 134 } 135 printf("\n"); 136 } 137 return 0; 138 }