SGU101 Domino(欧拉回路)

给定你n张骨牌,每张牌左右两端有一个数字,每张牌的左右两端数字可以颠倒,找出一种摆放骨牌的顺序,使得相邻骨牌的两端数字相同(最左边骨牌的最左端和最右边骨牌的最右端可以不管)。

最先想到把每个数字缩成点,然后发现这样连边都有问题,于是翻了翻各种题解,大概是这样的:把每个骨牌缩成两条边,分别对应正反两种情况,在数字之间连线,找出一种能够遍历所有骨牌的方案,然后就变成了一笔画问题。正向遍历骨牌的时候为+,反向遍历骨牌的时候为-,不管是正向还是反向遍历了这张骨牌,那么就视为这张骨牌已经遍历过了。

如样例:

5

1 2

2 4

2 4

6 4

2 1

这样连边:


如果有骨牌没有被遍历到,那么无解。

这一题我还有一点没有弄明白的地方,就是在保存路径的时候要先dfs下一条边,再保存当前遍历到的边。如果先保存当前遍历的边,再dfs下一条边,就WA了。

#include<cstdio>
#include<cstring>
using namespace std;
inline int mabs(int x)
{
	if(x > 0) return x;
	else return -x;
}
struct T
{
	int v,next,id;
}edge[250];
int head[250],cnt;
void add_edge(int u,int v,int id)
{
	edge[cnt].v = v;
	edge[cnt].id = id;
	edge[cnt].next = head[u];
	head[u] = cnt++;
}
int ans[250],tot,du[250];
bool vis[205];
void dfs(int u)
{
	for(int i = head[u]; i != -1; i = edge[i].next)
	{
		int v = edge[i].v;
		int id = edge[i].id;
		if(!vis[mabs(id)])
		{
			vis[mabs(id)] = 1;
			dfs(v);//先dfs下一条边,再保存当前访问的边
			ans[++tot] = id;//
		}
	}
}
int n,a,b,s = -1;
bool check()
{
	int count = 0;
	for(int i = 0; i <= 6; i++)
		if(du[i]&1)
		{
			count++;
			s = i;
		}
	if(count == 1||count > 2) return false;
	if(count == 2) return true;
	for(int i = 0; i <= 6; i++)
		if(du[i] > 0)
		{
			s = i;
			break;
		}
	return true;
}
int main()
{
	memset(head,-1,sizeof head);
	scanf("%d",&n);
	for(int i = 1; i <= n; i++)
	{
		scanf("%d%d",&a,&b);
		add_edge(a,b,i);
		add_edge(b,a,-i);
		du[a]++;
		du[b]++;
	}
	
	if(!check())
	{
		printf("No solution\n");
		return 0;
	}
	
	dfs(s);
	for(int i = 1; i <= n; i++)
		if(!vis[i])
		{	
			printf("No solution\n");
			return 0;
		}
	for(int i = tot; i >= 1; i--)//
		if(ans[i] > 0)
			printf("%d +\n",ans[i]);
		else
			printf("%d -\n",-ans[i]);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值