最短路径训练总结

最短路径

推荐这位高手总结的题,感觉还可以 最短路径题目汇总

这是我看到网上讲的比较详细的博客最短路径算法详解,看他的网址就晓得主要讲些啥子了。
最短路径有Bellman-FordDijkstraSPFAFloyd-Warshall算法 , 虽然还有啥子A*算法 ,但是我还没学,先把前头几个算法练好再说嘛,

优化

Dijkstra优化
虽然在学校头老师讲了Dijkstra和Floyd,但是Dijkstra的优化还没讲过,而且我们咋晓得啥子优化嘛,当时可以理解这些算法就不错了,Dijkstra有一个步骤是找当前最小的那个节点,然后通过这个节点更新与他连接的其他节点。就是这个找最小的节点的时候,复杂度是O(n),但是如果用对优化的话就可以提升到O(logn),就相当上档次了。

SPFA优化
SPFA其实就是用队列来维护,确实有点像BFS,又话可以看这篇博客SPFA优化
SPFA算法有两个优化算法 SLFLLL
SLF:Small Label First 策略,设要加入的节点是j,队首元素为i,若dist(j) < dist(i),则将j插入队首,否则插入队尾。
LLL:Large Label Last 策略,设队首元素为i,每次弹出时进行判断,队列中所有dist值的平均值为x,若dist(i)>x则将i插入到队尾,查找下一元素,直到找到某一i使得dist(i)<=x,则将i出对进行松弛操作。
但我的感觉就是spfa经常超时~

poj1511
这道题我确实还是学到东西了,这道题紧到超时,我把bellman,dijkstra,spfa都搞了一遍还是超时,最后我还是看答案了,学到了不少东西,首先最基本让我超时的就是输入,我再一次深刻体会到了cinscanf 的区别,输入量一大cin 是憋憋要超时啊~,然后就是邻接表,虽然数据小的用vector或者list都还是神的起,但是数据量一大还是不得行,然后我就从网上学了一招非常炫的,用数组就可以完成,数组索引

typedef struct Edge{
	int dest, value, next;
}Edge;
Edge edge[maxn];
int edgeindex[maxn];

void addedge(int sour, int dest, int value){
	edge[k].dest = dest;
	edge[k].value = value;
	edge[k].next = edgeindex[sour];
	edgeindex[sour] = k++;
}

只是通过两个数组,就轻轻松松的邻接表整好了,真的溜~然后便利也方便,就是通过之前建立好的索引for(int i = edgeindex[index]; i; i = edge[i].next)
最后就是这道题最溜的地方,可惜我看的答案,不是自己想出来的,题目要求所有自愿者去和回来的路程和最小,去的时候好办,就是1为源头的单源最短路径,但是回来咋办喃,我就是瓜兮兮的把所有的节点都求一道单源最短路径找他们回来的路程,哎~肯定是超时三,网上的解法是把之前所有的路径都反转过来,然后只用求一次1的单元最短就是所有节点回来的路程和了

#include<iostream>
#include<cstdio> 
#include<queue>
#include<cstring>
using namespace std;
#define maxn 1000005
typedef pair<int, int> P;
typedef struct Edge{
	int dest, value, next;
}Edge;
Edge edge[maxn];
int edgeindex[maxn];
int input[maxn][3];
int v, length;
int k = 1;
long long tag[maxn];
bool flag[maxn];

void addedge(int sour, int dest, int value){
	edge[k].dest = dest;
	edge[k].value = value;
	edge[k].next = edgeindex[sour];
	edgeindex[sour] = k++;
}

void dijkstra(){
	memset(tag, 0x3f, sizeof(tag));
	memset(flag, 0, sizeof(flag));
	tag[1] = 0; 
	priority_queue<P, vector<P>, greater<P> > que;
	que.push(P(0, 1));
	
	while(!que.empty()){
		P pp = que.top(); que.pop();
		int index = pp.second, temp = pp.first;
		flag[index] = 1;
		for(int i = edgeindex[index]; i; i = edge[i].next){
			int dest = edge[i].dest, value = edge[i].value;
			if(tag[dest] > tag[index] + value){
				tag[dest] = tag[index] + value;
				if(!flag[dest]) que.push(P(tag[dest], dest));
			}
		}
	}
	
//	for(int i = 1; i <= v; i++) cout << tag[i] << " " ; cout << endl;
}

int main(){
	int jishu;
	cin >> jishu;
	while(jishu --){
//		cin >> v >> length;
		//scanf
		scanf("%d%d", &v, &length);
		k = 1;
		memset(edge, 0, sizeof(edge));
		memset(edgeindex, 0, sizeof(edgeindex));
		int x, y, vv;
		for(int i = 0; i < length; i++){
//			cin >> input[i][0] >> input[i][1] >> input[i][2];
			//scanf
			scanf("%d%d%d", input[i], input[i]+1, input[i]+2);
			addedge(input[i][0], input[i][1], input[i][2]);
		}
		
		long long answer = 0;
		dijkstra();
		for(int i = 1; i <= v; i++) answer += tag[i];
		memset(edge, 0, sizeof(edge));
		memset(edgeindex, 0, sizeof(edgeindex));
		k = 1;
		for(int i = 0; i < length; i++){
			addedge(input[i][1], input[i][0], input[i][2]);
		}
		dijkstra();
		for(int i = 1; i <= v; i++) answer += tag[i];
		cout << answer << endl;
	}
	return 0;
}

教训

今天是真的惨~第一次感受到了G++C++ 编译器的区别,嗨呀,我确实遭整腾儿了~~~
同样的代码G++就要超时而C++就过了,而且我发现同样的代码,C++的效率始终都要比G++好的一些,有些时候是好得多,这只是效率有点差别,这还不是最大的问题,我今天还遇到一盘,相同的代码C++过了,而G++居然WA了~,说实话当时我紧到过不到,在代码找问题都要找疯了,总觉得自己就是对的嘛,凭啥喃,后头突然想到用C++提交一哈搞一哈喃,结果就找到问题了,然后我切网上搜,确实还是了解到些东西

这位高手的博客 G++和C++提交的区别 是好东西
我犯的错误就是用printf的时候,当要输出的数据是 Double类型的时候 ,特别要注意!!!虽然scanf的时候 double要用%lf 但是,输出的时候千万不能用 %lf ,要用%f
你可能会说不直接用cin 和 cout 喃~, 我上头已经遭过了,数据量已达就要超时,真的恼火感觉G++ 的优化确实不如 C++。还有就是在输入输出long long类型的时候在读和写的时候只支持scanf("%I64d", ...)printf("%I64d", ...).,这些都是 G++和C++提交的区别 这里讲的
还有就是我感觉G++对STL的支持感觉有些时候不理想啊,效率有点低,有些时候要自己手动来写比如说再写邻接表的时候,还有一次是些最小生成树,我用堆优化反而还超时了
poj2253 poj2240 我就是在做这两道题的时候遇到的这些问题

这里给出poj2253的代码,因为再找到最小生成树后去挨到挨到找路径的时候我以前只晓得用DFS,来便利,这哈学到了用path[]数组,在动态规划的时候就记录下路径后头再来找的时候就节约时间了

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<cmath>
using namespace std;
#define inf 0x3f3f3f3f
typedef pair<int, int> P;
int length;
double shuzu[201][201];
double input[205][2];
double tag[205];
bool flag[205];
int path[205];
double answer ;

void prim(){
	memset(tag, 0x7f, sizeof(tag));
	memset(flag, 0, sizeof(flag));
	memset(path, 0, sizeof(path));
	tag[1] = 0; 	
	
	for(int cishu = 0; cishu < length; cishu++){
		int index = -1;
		double zuixiao = inf;
		for(int i = 1; i <= length; i++){
			if(flag[i]) continue;
			if(zuixiao > tag[i]) zuixiao = tag[i], index = i;
		}
		
		if(index == -1 || index == 2) break;		
		flag[index] = 1;
		
		for(int i = 1; i <= length; i++){
			if(i == index) continue;
			double value = shuzu[index][i];
			int dest = i;
			if(!flag[i] && tag[i] > value){
				tag[i] = value;
				path[i] = index; 		
			}
		}
	}
			
	int index = 2;
	double temp;
	while(index != 1){
		temp = shuzu[index][path[index]];
		if(answer < temp) answer = temp;
		index = path[index];		
	}
}

int main(){
	int jishu = 1;
	while(cin >> length && length){		
	 	for(int i = 1; i <= length; i++){
//	 		cin >> input[i][0] >> input[i][1];
			scanf("%lf%lf", input[i], input[i]+1);
	 		for(int j = 1; j < i; j++){
			 	double r = pow(input[i][0]-input[j][0],2) + pow(input[i][1]-input[j][1], 2);
			 	shuzu[i][j] = shuzu[j][i] = r;			 	
			 }
			 shuzu[i][i] = 0;
		 }
		 
		 answer = 0;
		 prim();
		 answer = sqrt(answer);
		 printf("Scenario #%d\nFrog Distance = %.3f\n\n", jishu++,  answer);
	}
	return 0;	
}

poj2502 poj3013 poj3037
这三道题难度还是可以,我第一道题整的最久,紧到WA,然后发现了很多错,这道题其实就是把输入处理好,后头的最短路径都好办。处理输入的时候有以下几个问题。
1 、只有同一个线路的地铁才能按照地铁的速度来走
2 、不同的地铁线之间有可能有交叉
3 、 有可能不用坐地铁去学校,可能直接走到学校更快
4 、 同样的,有些地铁站也不一定要做地铁去,有可能地铁做了一大圈才到,走就几分钟
我当时还犯了个错,就申明数组大小的时候,应该是205 * 205 的 结果我算成了10025, 还有就是边应该适当的再申明大一点。
这段代码虽然很垃圾,但是过的时候还是很兴奋~

#include<iostream>
#include<queue>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define inf 0x3f3f3f3f
typedef struct Node{
	double x, y;	
	int id;
}Node;
typedef struct Edge{
	int dest, next;
	double value;
}Edge;
Edge edge[400500];
int edgeindex[400500];
Node node[400500];
int k = 1, length, len, number;
double tag[205], answer;
int flag[205];

void addedge(int sour, int dest, double value){
	edge[k].dest = dest;
	edge[k].value = value;
	edge[k].next = edgeindex[sour];
	edgeindex[sour] = k++;
}

int find(double x, double y){
	for(int i = 1; i < len; i++){
		if(x==node[i].x && y==node[i].y) return node[i].id;
	}
	return -1;
}


void spfa(){
	memset(tag, 0x7f, sizeof(tag));
	memset(flag, 0, sizeof(flag));
	tag[1] = 0; flag[1] = 1;
	queue<int> que;
	que.push(1);
	
	while(!que.empty()) {
		int index = que.front(); que.pop();
		flag[index] = 0;	
		for(int i = edgeindex[index]; i; i = edge[i].next){
			int dest = edge[i].dest;		
			double value = edge[i].value;
			if(tag[dest] > tag[index] + value){
				tag[dest] = tag[index] + value;
				if(!flag[dest]) que.push(dest), flag[dest] = 1;
			}
		}
	}
	
//	for(int i = 1; i < number; i++) cout << tag[i] << " "; cout << endl;
//	for(int i = 1; i < len; i++) cout << node[i].id << endl;
//	for(int i = 1; i < k; i++) cout << "(" << edge[i].next << ","  << edge[i].dest << ") ";cout << endl;
	answer = tag[2];
}

int main(){
	int jiji = 1;
	k = 1;
	cin >> node[1].x >> node[1].y >> node[2].x >> node[2].y;
	node[1].id = 1; node[2].id = 2;
	addedge(1, 2, sqrt(pow(node[1].x-node[2].x,2) + pow(node[1].y-node[2].y,2)) / 10000) ;
	length = 2, len = 2, number = 3;	
	int biaoji = 0, temp;
	double x, y, vv;
	while(cin >> x >> y){
		if(x==-1 && y==-1) {
			length = len;		
			biaoji = 0;
			jiji ++;
//			if(jiji == 3) break;
			continue;	
		}
		len ++;
		temp = find(x, y);
		if(temp == -1) node[len].id = number++;
		else node[len].id = temp;
		node[len].x = x; node[len].y = y;  
		for(int i = 1; i < len; i++){
			if(node[i].id == node[len].id) continue; 
			vv = sqrt(pow(node[i].x-x,2) + pow(node[i].y-y,2)) / 10000;
			addedge(node[i].id, node[len].id, vv);
			addedge(node[len].id, node[i].id, vv);
		}	
		
		if(biaoji){			
			vv = sqrt(pow(node[len-1].x-x,2) + pow(node[len-1].y-y,2)) / 40000;
			addedge(node[len].id, node[len-1].id, vv);
			addedge(node[len-1].id, node[len].id, vv);
		}
		biaoji = 1;
	}
	
	answer = inf;
	spfa();
	answer *= 60;
//	answer = ceil(answer);
//	cout << answer << endl;
	printf("%.0f\n", answer);
	return 0;
}

在整poj3037 SPFA超时了,然后我用heap+Dijkstra又过了,他们两个复杂度我还没有搞清楚,而且如果V(节点数)比较小的时候,比如啥子100啊那些,其实用heap优化反而还超时了,比如说上头的poj2253,虽然是prim但是就是dijkstra变一哈,我开始就是用heap优化,超时了,用纯dijkstra又过了。
poj3013 就要注意可以双向

poj3159
这道题真的不简单,但是这道题难的不是这道题本身有好灵活,方法有好炫,这道题提议的理解真的打脑壳,我开始读题就没咋懂,不懂的单词都差了,句子也查了,但是意思还是感觉没有理解透,然后我就感觉了一个意思:尽可能的连接边,在符合条件的情况下,使得最后所连接的边的和最大(尽可能多的让小朋友们都满意,然后求糖数量的最大情况),然后我就越想越不对越想越想越不对,感觉确实有点复杂,因为这就相当于求最大路径,而且还是负边权,上头那几个算法虽然有可以判断是否存在负边的,但是没法对负边权求路径三,还是最大路径,最大路径我也不晓得咋个整

然后我感觉还是不对,我就直接切网上搜(我没看答案,我只是想看哈那些高手的对题意的解读),题意是班上有n个同学,现在有一些糖要分给他们,设第i个同学得到的糖为p[i], 分糖必须满足条件:第i个同学要求第j个同学的糖不能超过自己k个,即p[j] - p[i] <= k,k >= 0。 要求在满足这些条件的情况下,求出p[n] - p[1]的最大值 然后我就看到一个关键词,差分约束(感觉应该单独出个专题),然后我就想啊,这得不得是啥子我没有学过的算法哦,那我肯定不能紧到在这上面浪费时间(为啥说是浪费时间喃?毕竟,想在短时间创造一个全新体系的算法还是相当产幻对我来说),然后我就直接且网上搜差分约束,我也没有仔细看,我看到差分约束的条件确实和这道题很像,然后我有看到一个关键词”这就是用最短路径或最长路径啊~“,然后我就马上把那个浏览器窗口关了,因为这确实可以用现在所学的东西来解决的,那我就不能切看答案了。

但是我还是没有摆脱最长路径的困扰,我总感觉就是用最长路径,毕竟是求第n个kid和第1个kid的最大差值,所以最长路径应该说的通三,而且我还想的如果x -> y >= 3 那 y -> x就应该为0,因为为了让差值最大的嘛,但却是还是有问题,然后我有看题,题中有句话要求在满足这些条件的情况下,就说是要满足所有人的条件,也就是说并不是看最大可p[n]有好大,而是看最小的那种情况,然后确实,就只是套最短路径的行了

poj3615
这是第一道题用Floyd-Warshall算法的,开始我想的使用最短路径,然后用path[]去记录,然后慢慢找,结果超时了,然后我又想要不然来个Prim和Floyd的结合。。。然后还是遇到一些问题WA了,最后我干脆直接就上Floyd,只不过这儿的Floyd使用来求最小生成树的

#include<iostream>
#include<cstring>
#include<queue>
#include<cstdio>
using namespace std;
#define maxn 25005
#define inf 0x3f3f3f3f
int v, length, T, k = 1;
int path[302][302];

void print(){
	cout << "__________" << endl;
	for(int i = 1; i <= v; i++){
		for(int j = 1; j <= v; j ++){
			if(path[i][j]==inf) cout << "0 ";
			else cout << path[i][j] << " ";
		}
		cout << endl;
	}
}

void flody(){
//	print();
	int zuida ;
	for(int k = 1; k <= v; k++){
		for(int i = 1; i <= v; i ++){
			for(int j = 1; j <= v; j ++){
				if(path[i][k]==inf || path[k][j]==inf) continue;
				zuida = path[i][k] > path[k][j] ? path[i][k] : path[k][j];				
				if(path[i][j] > zuida) path[i][j] = zuida;
			}
		}
	}
//	print();
}

int main(){
	cin >> v >> length >> T;
	k =  1;
	int x, y, vv;
	memset(path, 0x3f, sizeof(path));
	for(int i = 0; i < length; i++){
		scanf("%d%d%d", &x, &y, &vv);
		path[x][y] = vv;
//		addedge(x, y, vv);
	}
	
//	prim_Flody(); 
	flody();
	int start, end;
	for(int i = 0; i < T; i++){
		scanf("%d%d", &start, &end);
		if(path[start][end] == inf) cout << "-1" << endl;
		else cout << path[start][end] << endl;
	}
	return 0;
} 

poj3377
这道题不简单啊,因为这是我目前没有用最短路径套路做的题。先来一张这道题的图。
在这里插入图片描述

这道题就是求从一个点到另一个点的最短距离,第一反应自然是Dijkstra+heap,但是当我自信满满得敲完过后,给我来了个超时。但是说实话我有点震惊,但是看着道题的AC人数我其实还可以理解,然后我又用了SPFA,然后又超时,我当时感觉也正常,然后我又加上了前面SPFA的SLF和LLL优化,双管齐下。。。还是超时,当时我就感觉没得办法了~我会的所有技能都用了,整不出来得嘛,然后经过一个星期边上课边研究,有了点小想法,这道题其实相当于是在只有两行的方格中求最短路径,这个限制条件就有点蹊跷了,说不定就是不用最短路径算法,用类似DP的技术就解决了,真的炫!

首先不管那个点起始点哪个点是目的地,反正就把他当成是从左到右,是一回事,因为走的总路程长度是一样的三。然后就是对这两个端点预处理,为啥要预处理喃?那是因为要对两边先分别与处理。
首先假设如图,有下图所示:
假设点3是起始点,点1是终点,然后不管哪个是终点哪个是原点,就把最左边的那个当成源点把右边的那个当成终点,随意现在就变成从点1->点3,因为这道题的图有点有限制所以可以直接用dp,这个限制就是只有两行,而且第一行和第二行之间对应的点都能够联通,就相当于一个表格。所以dp完全就可以从左边那个点朝右边的那个点dp就行了,但是dp之前还有一个问题要解决,就是并不一定路径就是从左朝右,有可能刚开始的时候先朝左边走然后绕一圈回来,比如:如果1->2的距离大于从1这个点朝左走那三条线路的其中一个,那么 1->2的值就会被取代,因为毕竟先往左走还近些三
在这里插入图片描述

那为啥就一定是图中所示的三条向左走的路线喃?这是因为虽然会朝左走,但是始终会回来啊,也就是说先往左走的话始终都会回来也就是再朝右走的。而且一旦下来过后也就不会再向左走了,毕竟再向左的话到时候肯定还是要回来的,所以只会更远。求出了1->2的最短距离也就求而出了2->1的最短距离,所以如果2是起点的话也是同样的求法,而对于右边的终点也要对它先向右进行预处理,而这些与处理只用一层循环就可以解决,效率还是相当巴适的。

而接下来就是解决从起点到终点直接的动态规划,右下图所示:
现在为了画图更详细,我假设1是起点,3始终,而左右的预处理因为上面我已经讲过了,这里就集中精力解决从起点到终点的dp如何实现,因为从右向左dp的过程,是不会再向右走的(同上面讲的预处理道理一样,只会让总路径更长,所以只会嘲上下或者嘲右走),这样一来先把这之间的所有数组都初始化为inf,然后对于每一列的初始点来说直接先赋值为它左边的那个点,然后对这两个点分别求而出他们加上他们之间ferry的值(我这也来个传说中的状态转移方程嘛:arr[1][i] = arr[1][i-1]; arr[1][i] = min(arr[1][i], arr[2][i] + ferry[i]);
在这里插入图片描述

下图左边的结果是我用heap+dijkstra的效率,右边的这个是我用这个dp的效率截图,当然这个数据是我任意初始化的,但是ferry的数量都是最大的,这样才能最明显的看出效率差距,这哈就晓得为啥dijkstra超时了
在这里插入图片描述

但是这还没完,如果有点没有注意的话还是过不到,而且错误提示是:超时!!!,你肯定会说效率都提高差这么多为啥还是超时,哎~我就在这上面卡了好久,最后经过研究还是发现了问题。问题并不是出在这个dp算法上,而是输入输出上面,这道题最后提了一句说数据肯定能在64位整数内解决,我就为了保险起见,把用来接收输入的所有变量都定义为long long类型,而且我还专门注意了输入的时候不偷懒用cin,我用的都是scanf("%I64d", ...);但是输入还是超时了,但是当我把输入改成scanf("%d", ....);的时候居然就过了。这就再一次说明了输入确实有点烫。。。

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define maxn 1000005
#define inf 0x3f3f3f3f3f3f3f3f
int length;
int pos[2][2];
int ferry[maxn], walk[2][maxn];
long long shuzu[2][maxn];

int main(){
	while(~scanf("%d", &length) && length){
		memset(shuzu, 0x3f, sizeof(shuzu));
		for(int i = 0; i < 2; i++){
			scanf("%d%d", &pos[i][0], &pos[i][1]);
		}		
		if(pos[0][1] > pos[1][1]){
			int temp1 = pos[0][0], temp2 = pos[0][1];
			pos[0][0] = pos[1][0]; pos[0][1] = pos[1][1];
			pos[1][0] = temp1; pos[1][1] = temp2;
		}
		
		for(int i = 0; i < length; i++) scanf("%d", &walk[0][i]);
		for(int i = 0; i <= length; i++) scanf("%d", &ferry[i]);
		for(int i = 0; i < length; i++) scanf("%d", &walk[1][i]);		
		
		//pre dp
		int left = pos[0][1], right = pos[1][1];
		long long sum = 0, compare = inf, bridge = 0;
			//left
			for(int i = left-1; i >= 0; i --){
				sum = sum - bridge + walk[0][i] + walk[1][i] + ferry[i];
				bridge = ferry[i];
				if(compare > sum) compare = sum;
			}
			if(ferry[left] > compare) ferry[left] = compare;
			// right
			compare = inf; bridge = sum = 0;
			for(int i = right; i < length; i ++){
				sum = sum - bridge + walk[0][i] + walk[1][i] + ferry[i+1];
				bridge = ferry[i+1];
				if(compare > sum) compare = sum;
			}
			if(ferry[right] > compare) ferry[right] = compare;
			
		//dp					
		shuzu[pos[0][0]][pos[0][1]] = 0;
		shuzu[!pos[0][0]][pos[0][1]] = ferry[left];		
		left ++;
		while(left <= right){
			shuzu[0][left] = shuzu[0][left-1] + walk[0][left-1];
			shuzu[1][left] = shuzu[1][left-1] + walk[1][left-1];
			long long temp1 = shuzu[1][left] + ferry[left];
			long long temp2 = shuzu[0][left] + ferry[left];
			if(shuzu[0][left] > temp1) shuzu[0][left] = temp1;
			if(shuzu[1][left] > temp2) shuzu[1][left] = temp2;
			
			left ++;
		}
				
//		for(int i = 0; i < 2; i ++){
//			for(int j = 0; j <= length; j ++){
//				cout << shuzu[i][j] << " ";
//			}
//			cout << endl;
//		}
		
//		cout << shuzu[pos[1][0]][pos[1][1]] << endl;
		printf("%I64d\n", shuzu[pos[1][0]][pos[1][1]]);
	}
	return 0;
}

/*
1
1 1 0 0
2
1234 1234
2
*/

/*	TLE
1
1 0 0 0
2
1234 1234
2
*/

/*	TLE
1
0 0 1 0

*/

最后就是这道poj1724
这是我停了最久的题,结果后头突然反应过来一件事。。。
这道题是求求最短路径,但是求最短路径的时候还有个限制条件,就是走每条路都要给一定的钱,而我们初始得钱又很有限,求在这的情况下的最短路径。
这道题最恐怖的地方我局的是下面这句话:
在这里插入图片描述

Notice that different roads may have the same source and destination cities.

再咋个说我还是过了四级的人三(虽然考了三次才过),这句话还是没得生词得嘛,这句话的意思是:主义不同的路可能会有相同的起点和终点,我局的这样解释没得问题三,也就是说有重边!!!有重边的话这道题就不能用dijkstkra了,因为dijkstra的原理是:我之前求出来的点的最短距离是我求下一个点的基础,也就说我到前面的所有点都是最短距离了,我到下一个点才有可能是最短距离。但是这道题偏偏又有钱的限制,那就存在一个问题了
例如:
总共有10块钱
1->2 有两条路:(length:1,money:9) 、(length:5, money:5)
2->3 有两条路:(length:10, money:1)、(length:5, moeny:5)
那1->2的最短路径就是1,而已经花费了9块钱,那2->3喃? 只能用1块钱了,那1->3的距离就是1 + 10 = 11, 但其实应该是5 + 5 = 10才是最短距离,所以这里前面的最短并不能够是的后面的点在此基础上也达到最短,所以根本就不能用dijkstra,那看样子只能用spfa了,但是又有个问题,那就是假设有100个节点,每个节点都有100个重边,那还得了,肯定超内存。但是由于他说最多只有10000块钱,所以我想到的只能是前面动态规划的用一个长度为10000的数组来对应记录这样就会把数组长度有个限制,但是超市还是必然的嘛,这道题又只有一秒钟的时间。
所以我最后还是宁愿相信那句话的意思是:不同的路有相同的起点或者终点,也就是说在这里插入图片描述
也就是:
在这里插入图片描述
其实也有可能成圈,但是不在意嘛,反正没得重边就好办了~
然后我的做法是先从终点(所以要想把边反过来,因为是单向变),从终点先dijkstra,然后如果到1节点大于所拥有的money,就输出-1,然后再从1点dijkstra,而前面已经把到每个节点的限制都给出来了,就方便了

#include<iostream>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
#define maxn 10005
typedef pair<int, int> P;
typedef struct Edge{
	int dest, next, value, price;
}Edge;
typedef struct Node{
	int dest, value, price;
	Node(int d, int v, int p){dest = d; value = v; price = p;}	
	bool operator < (Node const &a) const{
		return value > a.value;
	}
}Node;
Edge edge[maxn];
int edgeindex[maxn];
int money, v, length, k = 1; 
int input[maxn][4];
int tag[105], qian[105];
bool flag[105];

void addedge(int sour, int dest, int value, int price){
	edge[k].dest = dest;
	edge[k].value = value;
	edge[k].price = price;
	edge[k].next = edgeindex[sour];
	edgeindex[sour] = k ++;
}

bool predijkstra(){
	memset(qian, 0x3f, sizeof(qian));
	memset(flag, 0, sizeof(flag));
	priority_queue<P, vector<P>, greater<P> > que;
	que.push(P(0, v));
	
	while(!que.empty()){
		P pp = que.top(); que.pop();
		int index = pp.second, temp = pp.first;
		if(flag[index]) continue;
		flag[index] = 1;
		qian[index] = temp;
		for(int i = edgeindex[index]; i; i = edge[i].next){
			int dest = edge[i].dest, price = edge[i].price;
			if(qian[dest] > temp + price)
				que.push(P(temp + price, dest));
		}
	}
		
//	for(int i = 1; i <= v; i++) cout << qian[i] << " " ;cout << endl;
	if(qian[1] > money) return true;
	else{
		for(int i = 1; i <= v; i++) qian[i] = money - qian[i];
		return false;
	}
}

void dijkstra(){
	int panduan[105];
	memset(tag, 0x3f, sizeof(tag));
	memset(panduan, 0x3f, sizeof(panduan));
	memset(flag, 0, sizeof(flag));
	priority_queue<Node> que;
	que.push(Node(1, 0, 0));
	
	while(!que.empty()){
		Node node = que.top(); que.pop();
		int index = node.dest, temp = node.value, jiage = node.price;
		if(flag[index]) continue;
		flag[index] = 1;
		tag[index] = temp;
		panduan[index] = jiage;		
		for(int i = edgeindex[index]; i; i = edge[i].next){
			int dest = edge[i].dest, value = edge[i].value, price = edge[i].price;
			int q = jiage + price;			
			if(q > qian[dest]) continue;
			int zhong = temp + value;
			if(zhong < tag[dest]){
				que.push(Node(dest, zhong, q));
			}else if(zhong == tag[dest] && q < panduan[dest]){
				que.push(Node(dest, zhong, q));
			}
		}
	}
	
//	for(int i = 1; i <= v; i++) cout << tag[i] << " "; cout << endl;
	cout << tag[v] << endl;
}

int main(){
	scanf("%d%d%d", &money, &v, &length);
	k = 1; 
	for(int i = 0; i < length; i++){
		scanf("%d%d%d%d", input[i], input[i]+1, input[i]+2, input[i]+3);
		addedge(input[i][1], input[i][0], input[i][2], input[i][3]);
	}
	
	if(predijkstra()) cout << "-1" << endl;
	else{
		k = 1;
		memset(edge, 0, sizeof(edge));
		memset(edgeindex, 0, sizeof(edgeindex));
		for(int i = 0; i < length; i++) 
			addedge(input[i][0], input[i][1], input[i][2], input[i][3]);
		dijkstra();
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Two_Punch/article/details/82920716
今日推荐