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;

}

 

 

你可能感兴趣的:(poj)