Team Them Up!

题意:简单地说,就是,一个N个节点的有向图,将节点分成两个集合,满足以下四个条件:

1。每个节点属于其中一个集合

2。每个集合至少有一个节点

3。集合里的每一个节点都有边连向同一个集合里的其他点

4。被分成的两个集合的大小要尽量接近

如果不能满足上述条件,输出 No solution ,否则输出这两个集合

 

刚开始也不知道怎么做,后来看了分析,其实弄懂了也不是太难,我还WA了这么多次,杯具!很不错的一道题啊,呵呵。。值得一练 !

说重点:

首先,求原图的补图,同时把有向图转化为无向图,即:对于节点u, v,如果没有边<u, v>或<v, u>,则建无向边(u, v)

分析一下现在这个图,如果u, v相连,表明u, v不能在同一个集合里,对于这个问题我们就有了感觉,先求图的连通分量,对于同一个连通分量,我们用二分图着色法,把整个连通分量里的点分到两个集合里,当然,如果着色失败,则无解,输出 No solution ,否则,这m个连通分量就构成了m个集合对(xi, yi),xi表示第i个连通分量中着色为0的集合,yi表示第i个连通分量中着色为1的集合,这样问题就转化成:对这m个集合对,每对里选出一个集合,构成A集合,剩余的构成B集合,要求A,B大小尽量接近,这样我们就可以用动态规划来搞定了。

DP方程:dp[i][j] = (dp[i][j - cnt[i][0]] | dp[i][j - cnt[i][1]] ) (1 <= i <= scc, 0 <= j <= N / 2)

dp[i][j] = 1 表示前i个连通分支,可以构成符合要求的节点数为j的集合

这里暂且用scc表示连通分支个数,N表示总节点个数,cnt[i][0]表示第i个分支中被着色成0的节点个数,cnt[i][1]表示第i个分支中被着色成1的节点个数,同时记录dp路径,这样这道题就算彻底搞定了。。 呵呵

 

代码
1 #include<stdlib.h>
2 #include<stdio.h>
3 #include<string.h>
4  #define MM 20010
5  #define NN 105
6
7 typedef struct node{
8 int v;
9 struct node *nxt;
10 }NODE;
11 NODE edg[MM], *Link[NN];
12
13  int idx, N, time, scc;
14 char map[NN][NN];
15 char col[NN];
16 int dfn[NN];
17 int cnt[NN][2];
18 int ans[NN][2][NN];// 记录连通分支
19 char dp[NN][NN];
20 char pre[NN][NN];//记录dp路径
21 char flag[NN];
22
23 void Add(int u, int v){//建边
24 edg[idx].v = v;
25 edg[idx].nxt = Link[u];
26 Link[u] = edg + idx++;
27 edg[idx].v = u;
28 edg[idx].nxt = Link[v];
29 Link[v] = edg + idx++;
30 }
31
32 int dfs(int u){//着色过程
33 int v;
34 dfn[u] = ++time;
35 ans[scc][col[u]][++cnt[scc][col[u]]] = u;
36 for(NODE *p = Link[u]; p; p = p->nxt){
37 v = p->v;
38 if(dfn[v] == 0){
39 col[v] = !col[u];
40 if (dfs(v) == 0) return 0;//
41 }else{
42 if (v != u && col[v] == col[u]) return 0;
43 }
44 }
45 return 1;
46 }
47
48 void Dp(){
49 int i, j, t;
50 dp[0][0] = 1;
51 for(i = 1; i <= N; i++) dp[0][i] = 0;
52
53 for(i = 1; i <= scc; i++){
54 for(j = 0; j <= N / 2; j++){// j从0开始
55 dp[i][j] = 0;
56 t = j - cnt[i][0];
57 if(t >= 0 && dp[i - 1][t]){
58 dp[i][j] = 1;
59 pre[i][j] = 0;
60 }
61 t = j - cnt[i][1];
62 if(t >= 0 && dp[i - 1][t]){
63 dp[i][j] = 1;
64 pre[i][j] = 1;
65 }
66 }
67 }
68 }
69
70 void Print(){//输出
71 int t, tmp, i, j, k;
72 for (k = N / 2; k >= 1; k--){
73 if(dp[scc][k]) break;
74 }
75 if(k == 0) puts("No solution");
76 else{
77 int tmp = k;
78 memset(flag, 0, sizeof(flag));
79 for (i = scc; i >= 1; i--){
80 t = pre[i][tmp];
81 tmp = tmp - cnt[i][t];
82 for(j = 1; j <= cnt[i][t]; j++){
83 flag[ans[i][t][j]] = 1;
84 }
85 }
86 printf("%d", N - k);
87 for(i = 1; i <= N; i++){
88 if(flag[i] == 0) printf(" %d", i);
89 }
90 puts("");
91 printf("%d", k);
92 for(i = 1; i <= N; i++){
93 if(flag[i]) printf(" %d", i);
94 }
95 puts("");
96 }
97 }
98 void Solve(){
99 int i;
100 memset(dfn, 0, sizeof(dfn));//发现标号
101 memset(col, 0, sizeof(col));
102 memset(cnt, 0, sizeof(cnt));
103 time = scc = 0;
104 for (i = 1; i <= N; i++){
105 if(dfn[i] == 0){
106 ++scc;
107 if(dfs(i) == 0) break;
108 }
109 }
110 if(i <= N) puts("No solution");
111 else{
112 Dp();
113 Print();
114 }
115 }
116 int main()
117 {
118 int i, a, j;
119 scanf("%d", &N);
120
121 idx = 0; memset(Link, 0, sizeof(Link));
122 memset(map, 0, sizeof(map));
123 for (i = 1; i <= N; i++){
124 while(scanf("%d", &a), a){
125 map[i][a] = 1;
126 }
127 }
128 for(i = 1; i <= N; i++){
129 for(j = i + 1; j <= N; j++){
130 if(map[i][j] == 0 || map[j][i] == 0) Add(i, j);
131 }
132 }
133 Solve();
134 return 0;
135 }
136

 

posted on 2010-10-08 13:43  ylfdrib  阅读(1663)  评论(0编辑  收藏  举报