百练 / 2017研究生推免上机考试 F:Prime Path

题目来源:http://poj.org/problem?id=3126

Prime Path

----------------------------------------

总时间限制: 1000ms      内存限制: 65536kB

描述

The ministers ofthe cabinet were quite upset by the message from the Chief of Security statingthat they would all have to change the four-digit room numbers on theiroffices. 
— It is a matter of security to change such things every now and then, to keepthe enemy in the dark. 
— But look, I have chosen my number 1033 for good reasons. I am the Primeminister, you know! 
— I know, so therefore your new number 8179 is also a prime. You will just haveto paste four new digits over the four old ones on your office door. 
— No, it’s not that simple. Suppose that I change the first digit to an 8, thenthe number will read 8033 which is not a prime! 
— I see, being the prime minister you cannot stand having a non-prime number onyour door even for a few seconds. 
— Correct! So I must invent a scheme for going from 1033 to 8179 by a path ofprime numbers where only one digit is changed from one prime to the nextprime. 

Now, the minister of finance, who had been eavesdropping, intervened. 
— No unnecessary expenditure, please! I happen to know that the price of adigit is one pound. 
— Hmm, in that case I need a computer program to minimize the cost. You don'tknow some very cheap software gurus, do you? 
— In fact, I do. You see, there is this programming contest going on... Helpthe prime minister to find the cheapest prime path between any two givenfour-digit primes! The first digit must be nonzero, of course. Here is asolution in the case above. 

1033
1733
3733
3739
3779
8779
8179

The cost of thissolution is 6 pounds. Note that the digit 1 which got pasted over in step 2 cannot be reused in the last step – a new 1 must be purchased.

输入

One line with a positive number: the number of test cases(at most 100). Then for each test case, one line with two numbers separated bya blank. Both numbers are four-digit primes (without leading zeros).

输出

One line for each case, either with a number stating theminimal cost or containing the word Impossible.

样例输入

3

1033 8179

1373 8017

1033 1033

样例输出

6

7

0

-----------------------------------------------------

解题思路

题目的中心思想是:
欲从四位素数A变到四位素数B,每次改变一位,保证每次改变后得到的仍是四位素数,求最少的改动次数
将每个四位素数视为节点如果素数C改变一位能够得到素数D,则在CD之间连一条边,如此构成一个图G。原问题转化为求图G两点之间的最短路长度
由于图G的边没有权值,故采用广度优先搜索
广度优先搜索分为2步:搜索回溯,分别对应2个数据结构:
(1)
队列q:用于搜索过程,新的节点入队,这些节点的前驱节点出队
(2)
数组mark: 用于搜索过程和回溯过程,初始化为0表示没有访问过任何节点,下次如果访问到一个节点的mark不是0,说明访问过了就不要再访问了;访问过一个节点以后,令mark[thisNode]= fatherNode. 回溯的时候顺着mark往前访问到起始节点就能得到最短路。

特别注意:C/C++里"^"是异或不是乘方,没有适用于(int,int)的乘方函数,需要自己编写函数实现!

-----------------------------------------------------

代码

#include
#include
#include
#include
using namespace std;

int power(int a, int n)					// a^n, "^"在C++里是异或
{
	int i = 0, result = 1;
	if (n==0)
	{
		return 1;
	}
	for (i=0; i> num;
	vector cost;						// 输出结果序列
	for (i=0; i> src;							// 原来的门牌号
		cin >> dst;							// 现在的门牌号

		/*广度优先搜索
		将1000~9999间的每个素数看做一个节点
		将改动一位就能从素数A变成素数B的关系看做连接A和B的一条边
		问题转化为在图中搜索两点之间的最短路
		*/
		int mark[10000] = {0};							// 每个数的前驱节点
		queue BFS;									// BFS队列
		BFS.push(src);									// 将src压入队列
		mark[src] = src;								// 将src的前驱节点设为自己
		while (src != dst)
		{
			if (!BFS.empty())
			{
				item = BFS.front();							// 取出BFS队首元素
				BFS.pop();									// 出队列
				for (j=0; j<4; j++)							// 4位数字
				{
					for (k=0; k<10; k++)					// 每位改一下
					{
						dig = (item % power(10,j+1))/power(10,j);	// item的第j位
						if (k!=dig)							// 如果不是原来的数字
						{
							nItem = item + (k-dig) * power(10,j);// 换上新的第j位
							if (nItem<10000 && nItem>1000)	// 防止数组越界
							{
								if (mark[nItem] == 0)		// 如果该节点没有被访问过
								{
									mark[nItem] = item;		// 用mark数组记录nItem的前驱节点
									if (nItem == dst)		// 如果找到dst
									{
										goto outwhile;		// 用goto跳出深层循环
									}
									else if (isPrime(nItem))// 如果nItem是素数
									{
										BFS.push(nItem);	// 则压入队列	
									}
								}
							}
						}
					}
				}
			}
		}
outwhile:												// 回溯法求解搜索树深度
		cnt = 0;										// 深度计数器
		item = dst;										// 从dst开始反向回溯
		while(item != src)								// 尚未回溯到src
		{
			item = mark[item];							// 通过mark数组回溯
			cnt ++;
		}
		cost.push_back(cnt);
	}

	vector::iterator it;
	for (it=cost.begin(); it!= cost.end(); it++)
	{
		cout << *it << endl;
	}

	return 0;
}


你可能感兴趣的:(百练OJ/poj,图论)