[各种面试题] 直角路线遍历棋盘

有一个n*m(n和m都不超过50)的棋盘,有k个目标格子需要访问。需要访问的格子的横纵坐标存放在数组x[]y[]中(0<=x[i]<n, 0<=y[i]<m)。

遍历的规则为:
  1. 每一步只能从一个目标格子水平或者竖直跳跃移动到另一个目标格子。
  2. 连续的两步必须形成直角。即如果前一步是水平移动,那么下一步只能竖直移动。

问是否存在一种遍历顺序,使得每个目标格子有且被访问一次。

样例:k=8, x=[0, 0, 0, 0, 2, 2, 4, 4], y=[0, 2, 4, 6, 4, 6, 2, 4],对应于下图中A, B, C, D, F, E, G, H 8个目标格子,存在满足条件的遍历A->D->E->F->C->B->G->H。
样例说明

想了很久,终于想到归结到欧拉路径上了。
第一步当然是盼是否连通,然后第二步是判断欧拉路径的充要条件:
一个无向图存在欧拉路径,当且仅当    该图所有顶点的度数为偶数    或者   除了两个度数为奇数外其余的全是偶数

但是在这里,连接关系是有水平跟竖直两种,怎么把这个跟欧拉路径的度数结合起来,我没想通,这里参考了别人的一个观点:

 用 a 表示含有奇数个元素的行数, b 表示含有奇数个元素的列数, 那么 a+b 一定是偶数. 如果 a+b > 2, 那么不可能遍历, 理由类似于欧拉回路 把位于同一行或者同一列的点都连线, 如果整个图是不连通的, 那么也显然不能遍历. 剩下的就可以遍历了, 这可以用归纳法证明.


然后借用别人的这个总结,就可以编写代码了:

const int MAXN=51;
int graph[MAXN][MAXN];
bool isConnected(int x,int y );
void dfs (int x,int y );
bool existPath(vector<int> &x, vector<int> &y) {
	int nx=x.size(),ny=y.size();
    if ( nx<=1 )
		return true;
	memset(graph,0,sizeof(graph));
	for(int i=0;i<nx;i++)
		graph[x[i]][y[i]]=1;

	if (!isConnected(x[0],y[0]) )
		return false;
	int a=0,b=0;
	for(int i=0;i<MAXN;i++)
	{
		int num=0;
		for(int j=0;j<MAXN;j++)
			if ( graph[i][j]>0 )
				num++;
		if ( num&1 )
			a++;

		int tnum=0;
		for(int j=0;j<MAXN;j++)
			if ( graph[j][i] >0 )
				tnum++;
		if ( tnum&1 )
			b++;
	}
	return (a+b)<=2; 
}
void dfs (int x,int y )
{
	if (graph[x][y]!=1 )
		return;
	graph[x][y]=2;
	for(int i=0;i<MAXN;i++)
	{
		if ( graph[x][i]==1 )
			dfs(x,i);
		if ( graph[i][y]==1 )
			dfs(i,y);
	}
}
bool isConnected(int x,int y )
{
	dfs(x,y);
	for(int i=0;i<MAXN;i++)
		for(int j=0;j<MAXN;j++)
		{
			if ( graph[i][j]== 2)
				graph[i][j]=1;
			else if ( graph[i][j]==1 )
				return false;
		}
	return true;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值