思路转自http://blog.csdn.net/bobten2008/article/details/4546528
/*
非常经典的一道DP(0,1背包)题,一个小小的错误让我调了几个小时,思路如下:
1)首先对原图求补图,只有当原图中两个人i和j不同时认识时,在补图中rever[i][j] = rever[j][i] = true, 否则为false;
这样做非常巧妙,通过这个转换将陌生的问题转换为了熟悉的问题
2)利用dfs对补图求所有的连通分量,这样很容易看出,处于不同连通分量中的两个点肯定是可以组在一个队中的;在用dfs
求连通分量的同时对同一个连通分量中的点进行0,1着色,这样当求得一个连通分量后,这个连通分量就分成了d0和d1两组
点。这时候需要做一个判断,对于同一个连通分量中的两个点i和j,如果i和j的0,1着色相同且在补图中i和j之间相连,则此题
肯定无解(花个图很容易就明白了,很好证明)
3)接下来的工作就很熟悉了,相当于将所有连通分量里面的0和1着色组分别分配到1队和2队,里,是1队和2队之间的人数差最小。
这是个典型的0,1背包问题,第i个连通分量里的0,1着色组的数目分别表示为con(i, 0)和con(i, 1),则DP公式如下
f[c][curDiff + con[c][0] - con[c][1]] = true if f[c - 1][curDiff] = true;
f[c][curDiff - con[c][0] + con[c][1]] = true if f[c - 1][curDiff] = true;
dp的同时需要记录路径
dp完后,找出f[lastC]中绝对值最小的,然后往前推就可以得出两队各自的人数,并输出id即可
*/
以上转自http://blog.csdn.net/bobten2008/article/details/4546528
不多说,我的代码:
/** * Problem:POJ1112 * Author:Shun Yao * Time:2013.5.19 * Result:Accepted * Memo:DP */ #include <cstring> #include <cstdlib> #include <cstdio> namespace mine { long min(long x, long y) { return x < y ? x : y; } } const long Maxn = 105; long n, tot, s[3][Maxn]; char visited[Maxn], a[Maxn][Maxn], f[Maxn][Maxn]; class gnode { public: long v; gnode *next; gnode(long V, gnode *ne):v(V), next(ne) {} gnode() {} ~gnode() {} } *g[Maxn]; void add(long x, long y) { g[x] = new gnode(y, g[x]); g[y] = new gnode(x, g[y]); } void dfs(long x, char f) { if (!f) ++s[(long)visited[x]][tot]; for (gnode *e = g[x]; e; e = e->next) { if (!visited[e->v]) { visited[e->v] = 3 - visited[x]; dfs(e->v, f); } else if (visited[e->v] == visited[x]) { printf("No solution"); exit(0); } } } int main() { static long i, j, C, num; freopen("poj1112.in", "r", stdin); freopen("poj1112.out", "w", stdout); scanf("%ld", &n); for (i = 1; i <= n; ++i) { g[i] = 0; memset(a[i], 0, sizeof a[i]); while (scanf("%ld", &j), j) a[i][j] = 1; } for (i = 1; i <= n; ++i) for (j = 1; j < i; ++j) if (a[i][j] && a[j][i]); else add(i, j); memset(visited, 0, sizeof visited); for (i = 1; i <= n; ++i) { if (!visited[i]) { ++tot; s[0][tot] = i; s[1][tot] = s[2][tot] = 0; visited[i] = 1; dfs(i, 0); } } for (i = 0; i <= tot; ++i) memset(f[i], 0, sizeof f[i]); f[0][0] = 1; C = n >> 1; for (i = 1; i <= tot; ++i) for (j = mine::min(s[1][i], s[2][i]); j <= C; ++j) f[i][j] = j >= s[1][i] && f[i - 1][j - s[1][i]] || j >= s[2][i] && f[i - 1][j - s[2][i]]; for (j = C; !f[tot][j]; --j); memset(visited, 0, sizeof visited); num = 0; for (i = tot; i; --i) if (j >= s[1][i] && f[i - 1][j - s[1][i]]) { j -= s[1][i]; num += s[1][i]; visited[s[0][i]] = 1; dfs(s[0][i], 1); } else { j -= s[2][i]; num += s[2][i]; visited[s[0][i]] = 2; dfs(s[0][i], 1); } printf("%ld", num); for (i = 1; i <= n; ++i) if (visited[i] == 1) printf(" %ld", i); putchar('\n'); printf("%ld", n - num); for (i = 1; i <= n; ++i) if (visited[i] == 2) printf(" %ld", i); fclose(stdin); fclose(stdout); return 0; }