• 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/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出 原文链接,否则保留追究法律责任的权利。

  • 相关阅读:
    数组元素按指定的位置排序
    git修改历史提交的备注信息
    js常用遍历理解
    async await和promise的区别,和使用方法
    js检测邮箱格式,正则检测邮箱格式
    前端,es6中的promise异步方法,及用的场景
    JMter 压力测试工具简单使用及介绍
    Vue Config
    vue 文件上传
    Windows Redis集群搭建简单版
  • 原文地址:https://www.cnblogs.com/hsuppr/p/3086968.html
Copyright © 2020-2023  润新知