SGU 101. Domino (多米诺骨牌)

101. Domino

time limit per test: 0.25 sec. 
memory limit per test: 4096 KB

Dominoes – game played with small, rectangular blocks of wood or other material, each identified by a number of dots, or pips, on its face. The blocks usually are called bones, dominoes, or pieces and sometimes men, stones, or even cards.
The face of each piece is divided, by a line or ridge, into two squares, each of which is marked as would be a pair of dice...

The principle in nearly all modern dominoes games is to match one end of a piece to another that is identically or reciprocally numbered.

ENCYCLOPÆDIA BRITANNICA

Given a set of domino pieces where each side is marked with two digits from 0 to 6. Your task is to arrange pieces in a line such way, that they touch through equal marked sides. It is possible to rotate pieces changing left and right side.

Input

The first line of the input contains a single integer N (1 ≤ N ≤ 100) representing the total number of pieces in the domino set. The following N lines describe pieces. Each piece is represented on a separate line in a form of two digits from 0 to 6 separated by a space.

Output

Write “No solution” if it is impossible to arrange them described way. If it is possible, write any of way. Pieces must be written in left-to-right order. Every of N lines must contains number of current domino piece and sign “+” or “-“ (first means that you not rotate that piece, and second if you rotate it).

Sample Input

5
1 2
2 4
2 4
6 4
2 1

Sample Output

2 -
5 +
1 +
3 +
4 -

概述

    N(1 ≤ N ≤ 100)个多米诺骨牌,每个骨牌左右两侧分别有一个数(0~6整数);

    给定骨牌,输出一种排列骨牌的方案,可以旋转以调换左右两数,使骨牌间相邻的两数字相等。

思路

    把数字0~6看作节点,每个骨牌看作一条边,那么这个问题就转化为一笔画问题,即问题模型是欧拉图。

    容易看出,每个骨牌的合理排列方案,都对应欧拉图中的一个一笔画路径(欧拉路)。

    那么现在目标从找到一个骨牌的合理排列方案,转变为找到图的一个欧拉路。

    题中最多有7个节点,100条边。如果简单地使用DFS,其最大时间复杂度O(n^2),会超时。这时需要充分应用欧拉图的性质进行优化——

    关于一笔画问题有如下关键定理:

  • 一个图形能一笔画完成当且仅当其符合两个条件,即图形是封闭联通的,和图形中的奇点(与奇数条边相连的点)个数为0或2。

    及其延伸定理:

  • 给定一个欧拉图,若图形中的奇点个数为2,则欧拉路必定从一个奇点出发,于另一奇点结束;若图形中没有奇点,则任选起点,必定能找到欧拉路,且终点与起点相同。

    从上述定理可以看到,给定欧拉图,我们可以非常容易地确定一个起点,从这个起点一定可以找到一条欧拉路。


    接下来解决怎么找的问题。难道还要用深度或广度搜索吗?鉴于题目给出的节点数只有7,答案是,只要像无头苍蝇一样搜索就ok。下面对这一解法作简单的论证——

    首先,不论选择怎样的路径,无路可走的情况只会在撞到终点时发生,且仅发生一次。

    ——考虑每个节点的指数(节点引出的该图形的弧的条数),在去除已走过的边后,除终点外每个节点的指数都是偶数。这样在走入非终点节点时,必有下一条边可走。

    其次,在无路可走时,还未走过的边可分为一条条回路。

    ——此时每个节点的指数都是偶数。从任意可行节点出发,只有该节点指数为奇数,这样在走入该节点之前,必有下一条边可走。即从任意可行节点出发最终均会回到该节点,构成回路;且易知剔除该回路后,节点指数仍均为偶数。

    综上,欧拉路的路径可归纳为下图所示。

   

  • 回路也经过某些节点;
  • 注意即使欧拉路需要经过7个节点,具体的欧拉路“主干道”上也不一定是7个节点;
  • 假设从节点node1走出到节点node2 and so on. 若之后走回node1,那么看作这一过程是走过了node1的一个回路,而不是向终点迈进一个节点。

    最后还有一个重要的性质:    

    若在无路可走后从某一节点出发搜索,必然会搜索完经过该节点的所有回路。它的论述过程与前面论述类似。


    这时我们的思路就相当明了了。搜索步骤如下:

  1. 选取起点(判定解是否存在)
  2. 从起点出发随机选取路径搜索,直到无路可走
  3. 从每一节点出发随机选取路径搜索,得到回路,插入步骤2中该节点所在的任意位置
  • 回路的插入可以在输出时操作,节省内存操作时间。
  • 解存在判定也可以在输出时操作。
  • 理论上最多搜索7-1个节点,实际大部分回路会被前几个节点搜索完。
  • 时间复杂度O(m)

     

代码

    开头判定解,主干道有点长,而且按主干道搜索回路插入主干道..在codeforge上31ms,200kb,感谢样例

//#include<bits\stdc++.h> 
#include<iostream>
#include<list> 
typedef long long ll;
using namespace std; 

const int N = 100+5;

struct bridge{
	int a;
	int b;
	bool en;
}b[N];

class road{
public:
	int i;
	bool r;
	road(int ii,bool rr){
		i=ii;
		r=rr;
	}
};

list<road> l;
list<road>::iterator lp;
bool r[7][7]={0};
int c[7]={0};
int count=0;
int n,p=0,rp=0;

bool rtest()
{
	if(count<2) return true;
	bool e=1;
	while(e){
		e=0;
		for(int i=0;i<7;i++)
			for(int j=0;j<7;j++){
				if(i!=j && r[i][j])
					for(int k=0;k<7;k++)
						if(i!=k && j!=k && r[j][k] && !r[i][k]){
							r[i][k]=r[k][i]=1;
							e=1;
						}
			}
	}
	for(int i=0;i<7-1;i++)
		if(c[i]) for(int j=i+1;j<7;j++)
			if(c[j] && !r[i][j]) return false;
	return true;
}

int main()  
{
	int t=0;
	cin>>n;
	count=n;
	for(int i=0;i<n;i++){
		cin>>b[i].a>>b[i].b;
		r[b[i].a][b[i].b]=r[b[i].b][b[i].a]=1;
		b[i].en=true;
		c[b[i].a]++;
		c[b[i].b]++;
	}
	for(int i=0;i<7;i++) if(c[i]%2){
		p=i;
		t++;
	}
	if(t>2 || !rtest()){
		cout<<"No solution";
		return 0;
	}
	bool end;
	lp=l.begin();
	while(count){
		end=1;
		for(int i=0;i<n;i++) if(b[i].en)
			if(b[i].a==p){
				l.insert(lp,road(i,0));
				b[i].en=false;
				p=b[i].b;
				end=0;
				count--;
			}
			else if(b[i].b==p)
			{
				l.insert(lp,road(i,1));
				b[i].en=false;
				p=b[i].a;
				end=0;
				count--;
			}
		if(end){
			----lp;
			if(lp->r)
				p=b[lp->i].a;
			else
				p=b[lp->i].b;
			lp++;
		}
	}
	count=n;
	for(lp=l.begin();lp!=l.end();lp++){
		count--;
		cout<<lp->i+1<<" ";
		if(lp->r) cout<<'-';
		else cout<<'+';
		if(count) cout<<endl;
	}
	return 0;
}

http://www.cnblogs.com/hizcard/  转载请注明出处

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值