poj1112 dp

题意:有N个人要分成两组,已知每个人认识哪些人,限制是组内人员必须互相认识,要求输出一组两组人数差最小的分配方案。

思路:这道题稍微有点麻烦。要经过一下几个步骤的处理:

1.各个点之间,将只有互相认识的点连线,然后求补图。这样做方便找出相互认识的几个集合。这几个集合间可以随意组合,因为不同集合里的人是相互认识的。于是问题就变成求每个集合里人相互认识的问题了。

2.求每个集合里人相互认识的情况:这里反正就用一遍广度优先搜索,因为有边相连的肯定是互不认识的。其实只要用一次广搜,不仅把各个集合给分开了,集合内的二分染色问题也解决了。不过碰到不能两种颜色染好的,就gameover。

3.用动态规划,其实就是一道0,1背包问题.动态规划公式如下:

             int tt=j-s[i].n1;
            if(tt>=0&&dp[i-1][tt])
            {
                dp[i][j]=1;//置1表示可以得到这种情况
            }
             tt=j-s[i].n2;
            if(tt>=0&&dp[i-1][tt])
            {
                dp[i][j]=1;                
            }

上面i表示第i个集合,j表示所拥有的元素数量,s[i].n1表示第i个集合中,第一种分类元素个数。s[i].n2表示第i个集合中,第二种分类元素个数。注意1<=j<=n/2,因为最后所求是要两个集合元素个数相近,又因为必有一个集合的元素个数<=n/2,所以最后就j由大往小搜索,搜到最大的可行j,即可。

#include<cstdio>
#include<cstring>
#include<vector>
#include<iterator>
#include<iostream>
#include<queue>
using namespace std;
int map[150][150],mp[150][150],vis[150],cnt,color[150];
struct node
{
	int n1,n2;
	int a[105],b[105];
}s[105];
int pre[105][105],dp[105][105],pre2[105][105];
struct node2
{
	int num,color;
}q[105];
queue<node2> p;
int main()
{
	int n,t;
	//FILE *fp=fopen("t.txt","r");
	scanf("%d",&n);
	memset(map,0,sizeof(map));
	memset(mp,0,sizeof(mp));
	for(int i=1;i<=n;i++)
	{
		while(1)
		{
			scanf("%d",&t);
			if(t==0)
			break;
			map[i][t]=1;
		}
	}
	for(int i=1;i<=n-1;i++)
	{
		for(int j=i+1;j<=n;j++)
		{
			if(map[i][j]==1&&map[j][i]==1)
			{
				mp[i][j]=mp[j][i]=1;
			}
		}
	}
	memset(vis,0,sizeof(vis));
	 while(!p.empty())
	 {
	 	p.pop();
	 }
	 node2 tp;
	 cnt=1;
	for(int i=1;i<=n;i++)
	{		
		if(!vis[i])
		{
			q[i].num=i; q[i].color=0;
			vis[i]=cnt;
			s[cnt].n1=s[cnt].n2=0;
			p.push(q[i]);
			while(!p.empty())
			{
				tp=p.front();
				p.pop();
				if(!tp.color)
				{
					s[cnt].a[s[cnt].n1]=tp.num;
					s[cnt].n1++;
				}
				else
				{
					s[cnt].b[s[cnt].n2]=tp.num;
					s[cnt].n2++;
				}
				for(int j=1;j<=n;j++)
				{
					if(!mp[j][tp.num]&&!vis[j])
					{
						vis[j]=cnt;
						q[j].num=j; q[j].color=tp.color^1;
						p.push(q[j]); 
					}else if(!mp[j][tp.num]&&vis[j])
					{
						if(tp.color==q[j].color&&tp.num!=q[j].num)
						{
							printf("No solution\n");
							return 0;
						}
					}
				}
			}
			cnt++;
		}
	}
	
/******************dp*******************************/
	int tt;
	memset(dp,0,sizeof(dp));
	dp[0][0]=1;
	for(int i=1;i<cnt;i++)
	{
		for(int j=1;j<=n/2;j++)
		{
			 tt=j-s[i].n1;
			if(tt>=0&&dp[i-1][tt])
			{
				dp[i][j]=1;
				pre[i][j]=0;
				pre2[i][j]=tt;
			}
			 tt=j-s[i].n2;
			if(tt>=0&&dp[i-1][tt])
			{
				dp[i][j]=1;
				pre[i][j]=1;
				pre2[i][j]=tt;
				
			}	
		}
	}
	int r;
	for(int i=n/2;i>=1;i--) if(dp[cnt-1][i]){r=i;break;}
	//cout<<r<<endl;
	int step1[101],step2[101],cnt1=0,cnt2=0;
	for(int i=cnt-1;i>=1;i--)
	{
		if(!pre[i][r])
		{
			for(int j=0;j<s[i].n1;j++)
			step1[cnt1++]=s[i].a[j];
			for(int j=0;j<s[i].n2;j++)
			step2[cnt2++]=s[i].b[j];			
		}else
		{
			for(int j=0;j<s[i].n2;j++)
			step1[cnt1++]=s[i].b[j];
			for(int j=0;j<s[i].n1;j++)
			step2[cnt2++]=s[i].a[j];			
		}
		r=pre2[i][r];
	}
	printf("%d ",cnt1);
	for(int i=0;i<cnt1;i++)
	printf("%d ",step1[i]);
	printf("\n");
	printf("%d ",cnt2);
	for(int i=0;i<cnt2;i++)
	printf("%d ",step2[i]);
	return 0;
 } 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值