ACM -- 魔兽传说

题目描述

 在古老的魔兽传说中,有两个军团,一个叫天灾,一个叫近卫。在他们所在的地域,有n个隘口,编号为1..n,某些隘口之间是有通道连接的。其中近卫军团在1号隘口,天灾军团在n号隘口。某一天,天灾军团的领袖巫妖王决定派兵攻打近卫军团,天灾军团的部队如此庞大,甚至可以填江过河。但是巫妖王不想付出不必要的代价,他想知道在不修建任何通道的前提下,部队是否可以通过隘口及其相关通道到达近卫军团展开攻击;如果可以的话,最少需要经过多少通道。由于n的值比较大(n<=1000),于是巫妖王找到了擅长编程的你 =_=,请你帮他解决这个问题,否则就把你吃掉变成他的魔法。为了拯救自己,赶紧想办法吧。

 

输入

 输入包含多组,每组格式如下。

第一行包含两个整数n,m(分别代表n个隘口,这些隘口之间有m个通道)。

下面m行每行包含两个整数a,b;表示从a出发有一条通道到达b隘口(注意:通道是单向的)。

 

输出

 如果天灾军团可以不修建任何通道就到达1号隘口,那么输出最少经过多少通道,否则输出NO。

 

示例输入

2 1
1 2
2 1
2 1

示例输出

NO
1

 

这道题代码没有AC过,找不到在线系统中有这道题的,有谁找到了帮忙给我发个站内信或者留言,谢谢!!


解题思路(主要两个需要考虑的地方,1. 小心会有环路出现;2. 就是最后一个点的处理,相等的时候会出现各种问题):

1. 其实,将题目剖析下就可以清楚的明白,这个只是一个求最短距离的问题,将每个隘口看成是一个点,然后通道就是连接两个点的线,注意这边是单方向的,这样就可以简单化题目了;

2. 因为这边使用的是宽度优先遍历,所以造了两个栈,一个用来存放遍历过的边我们称作边栈,一个用来存放部分解存放的是点的信息,称作点栈;

3. 从后往前循环遍历每个顶点,看是否有边存在,如果存在就将边压入边栈

4. 然后再从边栈中弹出一条边,进行判断,当这条边加进点栈是否会构成环路,(环路的判断:就是判断点栈中是否已经存在这个点,如果存在就环路,不存在就不会)

5. 后面就是一些细节方面的处理了,比如说,栈堆上的指针的处理问题了,怎么判断是否是最短路径啊等等扥。


#include "iostream"
using namespace std;

#define N 1001

typedef struct {
	int front;
	int behind;
}PassWay;

int iPassRoad = 0;//通道个数
int iPass = 0;//隘口个数
int iMatrix[N][N];
int iResult[N];
//int iVisited[N];
int iPassRoadNum = 0;
int iMinPassRoad = 99999;
int i, j;
PassWay pwStack[N];
int iStackTop = 1;

void MakeMatrix() 
{
	cin >> iPass >> iPassRoad;
	iMinPassRoad = 99999;
	iPassRoadNum = 0;
	iStackTop = 1;
	//iPassRoadNum = 9999;

	for (i = 0; i < iPass + 1; ++i)
	{
		for (j = 0; j < iPass + 1; ++j)
		{
			iMatrix[i][j] = 0;
		}
	}

	for (i = 1; i < iPassRoad + 1; ++i)
	{
		int x, y;
		cin >> x >> y;
		iMatrix[y][x] = 1;
	}
}

bool isExit(int num)
{
	for (int h = 0; h < iPassRoadNum + 1; ++h)
	{
		if (iResult[h] == num)
		{
			return true;
		}
	}
	return false;
}

int SearchMin() 
{
	PassWay pw;
	iResult[iPassRoadNum] = iPass;
	j = iPass;
 	while (iStackTop > 0)
	{
		if (j != 1)
		{
			for (i = 1; i < iPass + 1; ++i)
			{
				if (iMatrix[i][j] == 1)
				{
					PassWay pw;
					pw.front = j;
					pw.behind = i;
					pwStack[iStackTop++] = pw;
				}
			}
// 
// 			for (int h = 0; h < iStackTop; ++h)
// 			{
// 				cout << pwStack[h].front << "," << pwStack[h].behind << "<<" ;
// 			}
// 			cout << endl;
// 			for (int h = 0; h < iPassRoadNum + 1; ++h)
// 			{
// 				cout << iResult[h] << "<<" ;
// 			}
// 			cout << endl;

			do
			{
				pw = pwStack[--iStackTop];
				pwStack[iStackTop].behind = 0;
				pwStack[iStackTop].front = 0;
			}while (isExit(pw.behind));

			while (pw.front != iResult[iPassRoadNum] )
			{
				iResult[iPassRoadNum] = 0;
				--iPassRoadNum;
			}
		}
		if (pw.front == iResult[iPassRoadNum])
		{
			iResult[++iPassRoadNum] = pw.behind;
			j = pw.behind;
		}
// 
// 		for (int h = 0; h < iStackTop; ++h)
// 		{
// 			cout << pwStack[h].front << "," << pwStack[h].behind << "<<" ;
// 		}
// 		cout << endl;
// 		for (int h = 0; h < iPassRoadNum + 1; ++h)
// 		{
// 			cout << iResult[h] << "<<" ;
// 		}
// 		cout << endl;


		if (j == 1)
		{
			if (iPassRoadNum < iMinPassRoad)
			{
				iMinPassRoad = iPassRoadNum;
			}

			//cout << iMinPassRoad << endl;
			pw = pwStack[--iStackTop];
			while (pw.front != iResult[iPassRoadNum])
			{
				iResult[iPassRoadNum] = 0;
				--iPassRoadNum;
			}
			iResult[++iPassRoadNum] = pw.behind;
			j = pw.behind;
		}

// 		for (int h = 0; h < iStackTop; ++h)
// 		{
// 			cout << pwStack[h].front << "," << pwStack[h].behind << "<<" ;
// 		}
// 		cout << endl;
// 		for (int h = 0; h < iPassRoadNum + 1; ++h)
// 		{
// 			cout << iResult[h] << "<<" ;
// 		}
// 		cout << endl;
// 		cout << "MinNum=" << iMinPassRoad << endl;
	}
	return iMinPassRoad;
}

int main()
{
	//1. 构建邻接表
	while (1)
	{
		MakeMatrix();

// 		for (i = 0; i < iPass + 1; ++i)
// 		{
// 			for (j = 0; j < iPass + 1; ++j)
// 			{
// 				cout << iMatrix[i][j] << "\t";
// 			}
// 			cout << endl;
// 		}

		//2. 寻找最短路径
		//起始隘口,终止隘口
		if ( SearchMin() == 99999)
		{
			cout << "NO" << endl;
		}
		else
		{
			cout << iMinPassRoad << endl;
		}
	}
	return 1;
}



                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值