素数路(堆优化Dijsktra算法)

 

  • 问题描述

        已知一个四位的素数,要求每次修改其中的一位,并且要保证修改的结果还是一个素数,还不能出现前导零。你要找到一个修改数最少的方案,得到我们所需要的素数。
例如把1033变到8179,这里是一个最短的方案:
1033
1733
3733
3739
3779
8779
8179
修改了6次。

  • 思路:

        素数化点建图,采用迪杰斯特拉寻找最短路

  • 代码
  • #define _CRT_SECURE_NO_WARNINGS 1
    #include<iostream>
    #include<algorithm>
    #include<queue>
    #include<cstring>
    using namespace std;
    #define N (int)10000
    typedef pair<int,int> PII;
    const int  maxn = 1e5+10;
    int n,m,prime[maxn],cnt,st,en;
    int e[N], ne[N], idx,h[N],dis[N];
    bool a[maxn];
    
    bool judge(int a,int b) {//判断两个数字是否仅相差一位不同
    	int t = 0;
    	for (int i = 1; i <= 1000; i*=10) {
    		
    		if (a % 10 != b % 10) {
    			t++;
    			if (t >= 2) {
    				return false;
    			}
    		}
    		a /= 10;
    		b /= 10;
    	}
    	return true;
    }
    void add(int num1,int num2) {//数组模拟链表,邻接表的创建
    	
    	e[idx] = num2;
    	ne[idx] = h[num1];
    	h[num1] = idx++;
    
    }
    void init() {
    	a[1] = 1;
    	for (int i = 2; i<=maxn; i++) {
    		if (!a[i]) {
    			prime[cnt++] = i;
    			if (!st&&i>1000) {//得到第一个大于1000的素数的位置st
    				st = cnt - 1;
    			}
    			if (!en&&i > 9999) {//得到9999以内最后一个素数
    				en = cnt - 2;
    			}
    		}
    		for (int j = 0; prime[j] <= maxn / i;j++) {//欧拉线性素数筛法
    			a[i*prime[j]] = true;
    			if (i%prime[j] == 0) {
    				break;
    			}
    		}
    	}
    }
    int dijkastra() {//利用基于堆优化的dijkastra算法寻找最短路
    	memset(a, 0, sizeof(a));
    	memset(dis, 0x3f, sizeof(dis));
    	dis[n] = 0;
    	priority_queue<PII, vector<PII>, greater<PII>>heap;//建立小根堆
    	heap.push({ 0,n });//起点到起点的距离为0
    	while (heap.size())
    	{
    		auto t = heap.top();//取出堆顶元素,即此时到起点最短的点
    		heap.pop();
    		int ver = t.second, distance = t.first;//堆元素是pair,第一个元素是距离,第二个是节点序号
    		if (a[ver])continue;//已经被访问过,取消此次循环
    		a[ver] = true;
    		for (int i = h[ver]; i != -1; i = ne[i]) {//遍历当前该点所有的邻接点,更新距离
    			int j = e[i];
    			if (dis[j] > dis[ver] + 1) {//需要更新距离
    				dis[j] = dis[ver] + 1;
    heap.push({ dis[j],j });//放进堆里面
    			}
    		}
    	}
    	if (dis[m] == 0x3f3f3f3f)return -1;//如果目标终点的距离仍为无穷大,则认为终点与起点之间不可达
    	return dis[m];
    }
    int main() {
    	init();//初始化素数数组
    	memset(h, -1, sizeof(h));//邻接表头节点数组均填入-1
    	//printf("%d", judge(1001,1002));
    	//printf("%d %d ", prime[st],prime[en]);
    	scanf("%d %d", &n, &m);//n为起点,m为终点
    	for (int i = st; i <= en; i++) {//遍历所有1000到9999之间的素数
    		for (int j = i + 1; j <= en; j++) {
    			if (judge(prime[i], prime[j])) {//i和j之间仅存在一位不同
    				
    			
    				add(prime[i], prime[j]);//添加相应的边
    				add(prime[j], prime[i]);
    				
    			}
    		}
    	}
    	
    	
    	cout << dijkastra() << endl;
    

  •  总结

2.最开始还是想着用枚举遍历的方法解决,后来感觉时间复杂度过大,于是采用将每一个素数看作一个点,利用寻找最短路的算法。这里采用了时间复杂度最低的堆优化的迪杰斯特拉算法。其中由于判断素数开了一个bool数组,最短路算法判断节点是否被访问也要一个bool数组,防止内存超限,直接将a数组用完重新赋值false。最开始建图的时候,邻接表总是出问题,一个点后面总是后面跟着错误的点,怎么都debug不出来,后来才发现是邻接表的数组开小了。

3.开数组一定要先判断范围,否则到最后调试也很难发现,血的教训。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值