摘自nocow
描述
多米诺骨牌,一种用小的方的木块或其他材料,每个都被一些点在面上标记,这些木块通常被称为骨牌。每个骨牌的面都被一条线分成两个方形,两边各有一定点数。
N个多米诺骨牌,每个骨牌左右两侧分别有一个0~6的整数(骨牌可以旋转以调换其左右两数),求一种把这些骨牌从左到右排列的方案,使得所有相邻的两数字相等(即左边骨牌右侧的数字等于右边骨牌左侧的数字)。
输入
第一行是一个整数N(1 ≤ N ≤ 100),表示骨牌的数量。接下来的N行描述每块骨牌,每块左右两边有不同的点数。
输出
如果无法安排,输出“No solution”。如果可能,输出任何一种,每行有一个数字,和“+”或“-”,前者代表不旋转,后者代表旋转。
样例输入
5
1 2
2 4
2 4
6 4
2 1
样例输出
2 -
5 +
1 +
3 +
4 -
解题思路:
不难发现,如果把0~6当作顶点,则每个多米诺骨牌就相当于一条边,比如有1,2两个数字的多米诺骨牌就可以连一条1-2和一条2-1的有向边,然后在边上存上骨牌的编号和正反即可构成一张图,接着做欧拉路径就行了
那么如何做欧拉路径呢?首先对于一个有向图,有欧拉路径无向图要求度为奇数的点的个数只能为0或2(显然我上面的构图法可以看做无向图,因为每一个骨牌都双向连了边,可以看做无向图判断,具体如何判断请自己考虑)可以参考网友博客http://blog.chinaunix.net/uid-26380419-id-3164913.html
然后就是求欧拉路径了,具体求法上面网友博客中很详细,请读者自己阅读吧。
(ps:这道题好像有点坑,因为可能会出现一个骨牌两面的数字一样,这样要特判,请自己考虑)
//本人是淳朴的C党
#include <stdio.h>
#include <math.h>
int n;
int a[102][4]={0};
int bian[8][8][102]={0};//存下的是骨牌的标号,如果是正面就存正的,否则乘以-1在存入数组
int f=0;
int num[8]={0};
int t[8]={0};
int sum=0;
int k[8][8]={0};
void done(int o)
{
int i,j,p;
for(i=0;i<=6;i++)
{
if(bian[o][i][0]-k[o][i]>0)
{
k[o][i]++;
k[i][o]++;
j=k[o][i];
done(i);
if(bian[o][i][j]>0)
p=bian[o][i][j];
else p=-bian[o][i][j];
printf("%d ",p);
if(bian[o][i][j]>0)
printf("-\n");
else printf("+\n");
}
}
return;
}
int main()
{
int i,j;
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d%d",&a[i][1],&a[i][2]);
num[a[i][1]]++;
num[a[i][2]]++;
bian[a[i][1]][a[i][2]][++bian[a[i][1]][a[i][2]][0]]=i;
bian[a[i][2]][a[i][1]][++bian[a[i][2]][a[i][1]][0]]=-i;
if(a[i][1]==a[i][2])
t[a[i][1]]+=2;
}
for(i=0;i<=6;i++)
if(num[i]>0)
f++;
if(f==1)
{
for(i=1;i<=n;i++)
printf("%d +\n",i);
return 0;
}
for(i=0;i<=6;i++)
{
if(num[i]==0)
continue;
if(num[i]-t[i]==0)
{
printf("No solution\n");
return 0;
}
if((num[i]-t[i])&1)
sum++;
}
if(sum!=2 && sum!=0)
{
printf("No solution\n");
return 0;
}
if(sum==2)
{
for(i=0;i<=6;i++)
{
if((num[i]-t[i])&1)
{
done(i);
break;
}
}
}
else
{
for(i=0;i<=6;i++)
{
if(num[i]>0)
{
done(i);
break;
}
}
}
return 0;
}