hdu5305 Friends(dfs+map/hash)

题目:http://acm.hdu.edu.cn/showproblem.php?pid=5305

题意:给定N个人和M条朋友关系,是朋友关系的两个人之间有两种联系方式online和offline。使每个人的online的数量和offline的数量相等,求方案数。

分析:由于M<=28,暴力枚举的话2^28很大,会超时。可以考虑把所有的状态平分成两半,即枚举前面M/2条关系,暴力求出前面的2^(M/2)种状态,然后枚举后面M/2条关系,暴力求出后面2^(M/2)种状态,再枚举后面一半的状态,对于每一种状态直接在前面一半的状态里面找出满足条件的即可。找状态用dfs比二进制枚举要快,查询的话map比hash方便。。。

代码:

#include <iostream>
#include <vector>
#include <cstdio>
#include <cstring>
#include <map>
using namespace std;
int N,M,cnt[20],buf[20],f[20],ans,tp;
struct node
{
	int a,b;
}e[200];
map <vector<int>,int> mp;
void dfs(int x,int y)
{
	if(x>y)
	{
		if(tp==1)
			mp[vector <int> (buf+1,buf+N+1)]++;
		else
		{
			for(int i=1;i<=N;i++)
				f[i]=(cnt[i]>>1)-buf[i];
			if(mp.find(vector <int> (f+1,f+N+1))!=mp.end())
				ans+=mp[vector <int> (f+1,f+N+1)];
		}
		return ;
	}
	buf[e[x].a]++;
	buf[e[x].b]++;
	if(buf[e[x].a]<=(cnt[e[x].a]>>1) && buf[e[x].b]<=(cnt[e[x].b]>>1))
		dfs(x+1,y);
	buf[e[x].a]--;
	buf[e[x].b]--;
	
	dfs(x+1,y);
}
int main()
{
	int ncase,i,j,z;
	scanf("%d",&ncase);
	while(ncase--)
	{
		scanf("%d%d",&N,&M);
		mp.clear();
		memset(cnt,0,sizeof(cnt));
		ans=0;
		for(i=1;i<=M;i++)
		{
			scanf("%d%d",&e[i].a,&e[i].b);
			cnt[e[i].a]++;
			cnt[e[i].b]++;
		}
		z=1;
		for(i=1;i<=N;i++)
			if(cnt[i]&1)
				z=0;
		if(!z)
		{
			printf("0\n");
			continue ;
		}
		tp=1;
		dfs(1,M/2);
		tp=2;
		dfs(M/2+1,M);
		printf("%d\n",ans);
	}
	return 0;
}

还有一种更简单的方法,直接dfs然后剪一下枝。如果一个人有x个朋友,那么暴力搜的过程中出现online的个数>x/2或者offline的个数>x/2就不满足条件也就不用搜了。

#include <iostream>
#include <cstdio>
using namespace std;

int on[10],off[10],d[10],N,M,ans,x[30],y[30];
void dfs(int cur)
{
	if(cur>M)
	{
		for(int i=1;i<=N;i++)
			if(on[i]!=off[i])
				return ;
		++ans;
		return ;
	}
	int u=x[cur],v=y[cur];
	if(on[u]<d[u]/2 && on[v]<d[v]/2)
	{
		on[u]++;on[v]++;
		dfs(cur+1);
		on[u]--;on[v]--;
	}
	if(off[u]<d[u]/2 && off[v]<d[v]/2)
	{
		off[u]++;off[v]++;
		dfs(cur+1);
		off[u]--;off[v]--;
	}
}
int main()
{
	int ncase,i,j,k;
	scanf("%d",&ncase);
	while(ncase--)
	{
		scanf("%d%d",&N,&M);
		memset(d,0,sizeof(d));
		for(i=1;i<=M;i++)
		{
			scanf("%d%d",&x[i],&y[i]);
			d[x[i]]++;
			d[y[i]]++;
		}
		ans=0;
		dfs(1);
		printf("%d\n",ans);
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值