poj1112 Team Them Up!

思路转自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;
}


作者:HSUPPR
出处:http://www.cnblogs.com/hsuppr/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出 原文链接,否则保留追究法律责任的权利。

原文地址:https://www.cnblogs.com/hsuppr/p/3086968.html