您的位置:首页 > 编程语言 > C语言/C++

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

2018-03-27 11:41 357 查看
题目来源: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
8179The 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.样例输入
31033 81791373 80171033 1033样例输出670-----------------------------------------------------
解题思路
题目的中心思想是:
欲从四位素数A变到四位素数B,每次改变一位,保证每次改变后得到的仍是四位素数,求最少的改动次数
将每个四位素数视为节点如果素数C改变一位能够得到素数D,则在C和D之间连一条边,如此构成一个图G。原问题转化为求图G两点之间的最短路长度
由于图G的边没有权值,故采用广度优先搜索
广度优先搜索分为2步:搜索回溯,分别对应2个数据结构:
(1) 队列q:用于搜索过程,新的节点入队,这些节点的前驱节点出队
(2) 数组mark: 用于搜索过程和回溯过程,初始化为0表示没有访问过任何节点,下次如果访问到一个节点的mark不是0,说明访问过了就不要再访问了;访问过一个节点以后,令mark[thisNode]= fatherNode. 回溯的时候顺着mark往前访问到起始节点就能得到最短路。
特别注意:C/C++里"^"是异或不是乘方,没有适用于(int,int)的乘方函数,需要自己编写函数实现!
-----------------------------------------------------
代码#include<iostream>
#include<vector>
#include<math.h>
#include<queue>
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<n; i++)
{
result *= a;
}
return result;
}

bool isPrime(int n)
4000
// 函数:判断一个数是否是素数
{
int i = 2; // 从2开始,不能从1开始
for (i=2; i<=sqrt(n); i++)
{
if (n%i==0)
{
return false;
}
}
return true;
}

int main()
{
int num = 0, i = 0, src = 0, dst = 0, j = 0, k = 0, cnt = 0,
item = 0, nItem = 0, dig = 0;
cin >> num;
vector<int> cost; // 输出结果序列
for (i=0; i<num; i++)
{
cin >> src; // 原来的门牌号
cin >> dst; // 现在的门牌号

/*广度优先搜索
将1000~9999间的每个素数看做一个节点
将改动一位就能从素数A变成素数B的关系看做连接A和B的一条边
问题转化为在图中搜索两点之间的最短路
*/
int mark[10000] = {0}; // 每个数的前驱节点
queue<int> 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<int>::iterator it;
for (it=cost.begin(); it!= cost.end(); it++)
{
cout << *it << endl;
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息