数据结构专题系列 基础篇-9-2 图论-最短路

数据结构专题系列 基础篇-9-2 图论-最短路

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

题目传送门

洛谷图论-最短路 普及- 组
洛谷图论-最短路 普及/提高- 组
--------------------------------------------------------------------------------
更多详见>>

OJ题解系列 目录导航帖

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

这里是数据结构专题系列 基础篇-9-2 图论-最短路
图论是一个大家族,本章我们讨论图论的其中一个分支——最短路问题,这个问题不仅最基础,而且算法也最广泛,包括了Dijkstra、Floyd、Bellman-Ford、SPFA等,基础部分着重讨论这4种算法,提高部分会有相应的算法扩展和补充

更多内容详见:OI WIKI
其中,
Dijkstra算法的时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)
Floyd算法的时间复杂度为 O ( n 3 ) O(n^3) O(n3)
Bellman-Ford算法的时间复杂度为 O ( n E ) O(nE) O(nE)
SPFA算法的时间复杂度为 O ( k E ) O(kE) O(kE),注意此处的k是不可忽略的常数,常数波动范围很大,从 2 − n 2-n 2n都有可能,因此,SPFA在某些特殊的图中会退化成Bellman-Ford算法!

本章一共1+25题,推荐先花若干时间学明白最短路思想,然后分2-4周完成习题,这样有助于巩固所学知识!
接下来就是题解部分了,每道算法题都标注有对应的算法标签,对于那些易错、较难或是测试点比较特殊的题目会着重标注,本章推荐的题目有:

P5651 基础最短路练习题 图论 + Dijkstra + Kruskal + DFS
P6833 [Cnoi2020]雷雨 图论 + Dijkstra + 枚举
其余题目 模板题

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

难度: 普及-

P2910 [USACO08OPEN]Clear And Present Danger S

算法标签: 图论 + Floyd
注意点: 全源最短路径应优先考虑Floyd算法

#include<bits/stdc++.h>
using namespace std;
int N,M;
int a[10005];
int dis[105][105];

int main(){
    
    
	scanf("%d%d",&N,&M);
	memset(dis,0x3f,sizeof(dis));
	for(int i=0;i<M;i++){
    
    
		scanf("%d",&a[i]);
	}
	for(int i=1;i<=N;i++){
    
    
		for(int j=1;j<=N;j++){
    
    
			scanf("%d",&dis[i][j]); 
		}
	}
	for(int k=1;k<=N;k++){
    
    
		for(int i=1;i<=N;i++){
    
    
			for(int j=1;j<=N;j++){
    
    
				if((i!=j) && (j!=k) && (k!=i)){
    
    
					dis[i][j] = min(dis[i][j],dis[i][k]+dis[k][j]);
				}
			}
		}
	}
	int ans = 0;
	for(int i=0;i<M-1;i++){
    
    
		ans += dis[a[i]][a[i+1]];
	}
	printf("%d\n",ans);
	return 0;
}

难度: 普及/提高-

P2935 [USACO09JAN]Best Spot S

算法标签: 图论 + Dijkstra + 枚举
注意点: 依次枚举每个顶点作为Bessie的出发点,做N轮Dijkstra,求最短路径和, ∑ 最 短 路 径 \sum 最短路径 最小的那个点即为Bessie的出发点

#include<bits/stdc++.h>
using namespace std;
int P,F,C;
int a[505];
bool vis[505];
struct node{
    
    
	int v;
	int dis;
};
node temp;
vector<node> G[505];
int dp[505];
void dijkstra(int u){
    
    
	priority_queue<pair<int,int> > q;
	dp[u] = 0;
	q.push(make_pair(0,u));
	while(!q.empty()){
    
    
		pair<int,int> f = q.top();
		q.pop();
		int u = f.second;
		if(vis[u]){
    
    
			continue;
		}
		vis[u] = true;
		for(int i=0;i<G[u].size();i++){
    
    
			int v = G[u][i].v;
			int w = G[u][i].dis;
			if(!vis[v] && dp[u]+w<dp[v]){
    
    
				dp[v] = dp[u] + w;
				q.push(make_pair(-dp[v],v));
			}
		}
	}
}
int main(){
    
    
	scanf("%d%d%d",&P,&F,&C);
	for(int i=1;i<=F;i++){
    
    
		scanf("%d",&a[i]);
	}	
	for(int i=1;i<=C;i++){
    
    
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		temp.v = v;
		temp.dis = w;
		G[u].push_back(temp);
		temp.v = u;
		G[v].push_back(temp);
	}
	int res = INT_MAX;
	int site = -1;
	for(int i=1;i<=P;i++){
    
    
		memset(dp,0x3f,sizeof(dp));
		memset(vis,0,sizeof(vis));
		dijkstra(i);
		int ans = 0;
		for(int j=1;j<=F;j++){
    
    
			ans += dp[a[j]];
		}
		if(ans < res){
    
    
			res  = ans;
			site = i;
		}
	}
	cout << site << endl;
	return 0;
}

P2984 [USACO10FEB]Chocolate Giving S

算法标签: 图论 + Dijkstra
注意点: Dijkstra模板题

#include<bits/stdc++.h>
using namespace std;

int N,M,B;
struct Edge{
    
    
	int value;
	int to;
	int next;
};

Edge e[200005];
int head[50010];
int cnt = 0;
int dis[50010];
bool vis[50010];

int s,t;

void addedge(int a,int b,int v){
    
    
	cnt++;
	e[cnt].value = v;
	e[cnt].to = b;
	e[cnt].next = head[a];
	head[a] = cnt;
	return;
}

void SPFA(){
    
    
	int u;
	queue<int> q;
	dis[1] = 0;
	vis[1] = true;
	q.push(1);
	while(!q.empty()){
    
    
		u = q.front();
		q.pop();
		vis[u] = false;
		for(int i=head[u];i;i=e[i].next){
    
    
			if(dis[u] + e[i].value < dis[e[i].to]){
    
    
				dis[e[i].to] = dis[u] + e[i].value;
				if(!vis[e[i].to]){
    
    
					vis[e[i].to] = true;
					q.push(e[i].to);
				}
			}
		}
	}
}
int main(){
    
    
	scanf("%d%d%d",&N,&M,&B);
	
	for(int i=1;i<=M;i++){
    
    
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		addedge(u,v,w);
		addedge(v,u,w);
	}
	
	memset(dis,0x3f,sizeof(dis));
	
	SPFA();
	
	for(int i=1;i<=B;i++){
    
    
		int s,t;
		scanf("%d%d",&s,&t);
		printf("%d\n",dis[s] + dis[t]);
	}
	return 0;
}

P1339 [USACO09OCT]Heat Wave G

算法标签: 图论 + Dijkstra
注意点: Dijkstra模板题

#include<bits/stdc++.h>
using namespace std;
int n,m,s,t1;
int head[25050];
int cnt = 0;
int dis[25050];

struct Edge{
    
    
	int to;
	int value;
	int next;
};
Edge edge[62050];

struct vertex{
    
    
	int v;
	int dis;
	vertex(){
    
    
		
	}
	vertex(int v1,int dis1){
    
    
		v = v1;
		dis = dis1;
	}
	bool operator < (const vertex &x)const{
    
    
		return x.dis < dis;
	}
};
priority_queue<vertex> q;

void addedge(int a,int b,int v){
    
    
	cnt++;
	edge[cnt].to = b;
	edge[cnt].value = v;
	edge[cnt].next = head[a];
	head[a] = cnt;
	return;
} 

int main(){
    
    
	scanf("%d%d%d%d",&n,&m,&s,&t1);	
	for(int i=0;i<m;i++){
    
    
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		addedge(u,v,w);
		addedge(v,u,w);
	}
	memset(dis,0x3f,sizeof(dis));
	dis[s] = 0;
	q.push(vertex(s,0));
	vertex t;
	while(!q.empty()){
    
    
		t = q.top();
		q.pop();
		for(int i=head[t.v];i!=0;i=edge[i].next){
    
    
			if(dis[t.v] + edge[i].value < dis[edge[i].to]){
    
    
				dis[edge[i].to] = dis[t.v] + edge[i].value;
				q.push(vertex(edge[i].to,dis[edge[i].to]));
			}
		}
	}
	printf("%d\n",dis[t1]);
	return 0;
} 

P5651 基础最短路练习题

算法标签: 图论 + Dijkstra + Kruskal + DFS
注意点: 一道好题!根据异或的性质不难发现, d i s ( x , y ) = a x   x o r   a y dis(x,y)=a_x\ xor \ a_y dis(x,y)=ax xor ay,因此,只需要DFS递推一遍所有的点权即可,不妨假设点1的点权为0,递推得到最终结果,再查询输出
那么这题为何用到最小生成树算法呢?因为题目中说对于所有的简单环,异或和都为0,这就意味着从点x到点y的所有简单路径长度都一致,也意味着只需保留一棵树,等价于计算了所有的顶点的最短距离,减少了时间复杂度(枚举边)

#include<bits/stdc++.h>
using namespace std;
int n,m,Q;
const int maxn = 1e5+5;
int father[maxn];
int dp[maxn];
bool vis[maxn];
struct node{
    
    
	int x,y;
	int dis;
};
struct edge{
    
    
	int v;
	int dis;
};
edge temp;
node t[2*maxn];
vector<edge> G[maxn];
int cnt = 0;

int findfather(int x){
    
    
	if(x == father[x]){
    
    
		return x;
	}
	int F = findfather(father[x]);
	father[x] = F;
	return F;
}
void Kruskal(){
    
    
	int ans = 0;
	for(int i=1;i<=n;i++){
    
    
		father[i] = i;
	}
	for(int i=0;i<cnt;i++){
    
    
		int fa = findfather(t[i].x);
		int fb = findfather(t[i].y);
		if(fa!=fb){
    
    
			father[fa] = fb;
			temp.v = t[i].y;
			temp.dis = t[i].dis;
			G[t[i].x].push_back(temp);
			temp.v = t[i].x;
			G[t[i].y].push_back(temp);
			ans++;
		}
		if(ans == n-1){
    
    
			break;
		}
	}
}
void dfs(int u){
    
    
	vis[u] = true;
	for(int i=0;i<G[u].size();i++){
    
    
		int v = G[u][i].v;
		int w = G[u][i].dis;
		if(!vis[v]){
    
    
			dp[v] = dp[u] ^ w;
			dfs(v);
		}
	}
}
int main(){
    
    
	scanf("%d%d%d",&n,&m,&Q);
	for(int i=1;i<=m;i++){
    
    
		scanf("%d%d%d",&t[cnt].x,&t[cnt].y,&t[cnt].dis);
		cnt++;
	}
	Kruskal();
	dp[1] = 0;
	dfs(1);
	for(int i=1;i<=Q;i++){
    
    
		int x,y;
		scanf("%d%d",&x,&y);
		printf("%d\n",dp[x]^dp[y]);
	}
	
	return 0;
}

P1576 最小花费

算法标签: 图论 + Dijkstra
注意点: Dijkstra模板题!

#include<bits/stdc++.h>
using namespace std;

int n,m;

struct Edge{
    
    
	double value;
	int to;
	int next;
};

Edge e[400010];
int head[2005];
bool vis[2005];
double dis[2005];

int cnt = 0;
int A,B;

void addedge(int a,int b,double v){
    
    
	cnt++;
	e[cnt].to = b;
	e[cnt].value = v;
	e[cnt].next = head[a];
	head[a] = cnt;
	return;
}
void SPFA(){
    
    
	int u;
	queue<int> q;
	q.push(A);
	dis[A] = 1.0;
	vis[A] = true;
	while(!q.empty()){
    
    
		u = q.front();
		q.pop();
		vis[u] = false;
		for(int i=head[u];i!=0;i=e[i].next){
    
    
			if(dis[u]*e[i].value>dis[e[i].to]){
    
    
				dis[e[i].to] = dis[u] * e[i].value;
				if(!vis[e[i].to]){
    
    
					q.push(e[i].to);
					vis[e[i].to] = true;
				}
			}
		}
	}
}
int main(){
    
    
	scanf("%d%d",&n,&m);
	fill(dis+1,dis+n+1,0);
	for(int i=0;i<m;i++){
    
    
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		double d = (double)(100-w) * 1.0 / 100.0;
		addedge(u,v,d);
		addedge(v,u,d);
	}
	scanf("%d%d",&A,&B);
	SPFA();
	printf("%.8lf\n",100.0/dis[B]);
	return 0;
} 

P1821 [USACO07FEB] Cow Party S

算法标签: 图论 + Dijkstra
注意点: Dijkstra模板题!

#include<bits/stdc++.h>
using namespace std;
int n,m,x;
struct node{
    
    
	int v;
	int dis;
};
node temp;
vector<node> G1[1005];
vector<node> G2[1005];
int dp1[1005];
int dp2[1005];
bool vis[1005];
int ans = 0;
void dijkstra(int u,vector<node> G[1005],int dp[1005]){
    
    
	memset(vis,0,sizeof(vis));
	priority_queue<pair<int,int> > q;
	dp[u] = 0;
	q.push(make_pair(-dp[u],u));
	while(!q.empty()){
    
    
		pair<int,int> f = q.top();
		q.pop();
		int u = f.second;
		if(vis[u]){
    
    
			continue;
		} 
		vis[u] = true;
		for(int i=0;i<G[u].size();i++){
    
    
			int v = G[u][i].v;
			int w = G[u][i].dis;
			if(!vis[v] && dp[u] + w < dp[v]){
    
    
				dp[v] = dp[u] + w;
				q.push(make_pair(-dp[v],v));
			}
		}
	}	
} 
int main(){
    
    
	scanf("%d%d%d",&n,&m,&x);
	for(int i=1;i<=m;i++){
    
    
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		temp.v = v;
		temp.dis = w;
		G1[u].push_back(temp);
		temp.v = u;
		G2[v].push_back(temp);
	} 
	memset(dp1,0x3f,sizeof(dp1));
	memset(dp2,0x3f,sizeof(dp2));
	dijkstra(x,G1,dp1);
	dijkstra(x,G2,dp2);
	for(int i=1;i<=n;i++){
    
    
		ans = max(ans,dp1[i] + dp2[i]);
	}
	cout << ans << endl;
	return 0;
} 

P1744 采购特价商品

算法标签: 图论 + Dijkstra
注意点: Dijkstra模板题!

#include<bits/stdc++.h>
using namespace std;
int n,m;
int S,T;
const double INF = 1e20;
struct site{
    
    
	int x,y;
};

site s[105];
double dis[105];
int head[105];
bool vis[105];
struct Edge{
    
    
	int to;
	int next;
	double value;	
};
Edge e[2005];
int cnt = 0;

void addedge(int a,int b,double v){
    
    
	cnt++;
	e[cnt].value = v;
	e[cnt].to = b;
	e[cnt].next = head[a];
	head[a] = cnt;
	return;
}

double distance1(site s1,site s2){
    
    
	double res;
	res = sqrt((s1.x-s2.x)*(s1.x-s2.x)+(s1.y-s2.y)*(s1.y-s2.y));
	return res;
}

void SPFA(){
    
    
	int u;
	dis[S] = 0.0;
	vis[S] = true;
	queue<int> q;
	q.push(S);
	while(!q.empty()){
    
    
		u = q.front();
		vis[u] = false;
		q.pop();
		for(int i=head[u];i!=0;i=e[i].next){
    
    
			if(dis[u] + e[i].value < dis[e[i].to]){
    
    
				dis[e[i].to] = dis[u] + e[i].value;
				if(!vis[e[i].to]){
    
    
					q.push(e[i].to);
					vis[e[i].to] = true;
				}
			}
		}
	}
}
int main(){
    
    
	scanf("%d",&n);
	fill(dis+1,dis+n+1,INF);
	for(int i=1;i<=n;i++){
    
    
		scanf("%d%d",&s[i].x,&s[i].y);
	}
	scanf("%d",&m);
	for(int i=1;i<=m;i++){
    
    
		int u,v;
		scanf("%d%d",&u,&v);
		double dis = distance1(s[u],s[v]);
		addedge(u,v,dis);
		addedge(v,u,dis);
	}
	scanf("%d%d",&S,&T);
	SPFA();
	printf("%.2lf",dis[T]);
	return 0;
} 

P2299 Mzc和体委的争夺战

算法标签: 图论 + Dijkstra
注意点: Dijkstra模板题!

#include<bits/stdc++.h>
using namespace std;
int n,m;
struct node{
    
    
	int v;
	int dis;
};
node temp;
vector<node> G[2505];
int dp[2505];
bool vis[2505];
void dijstra(int u){
    
    
	memset(dp,0x3f,sizeof(dp));
	memset(vis,0,sizeof(vis));
	dp[u] = 0;
	priority_queue<pair<int,int> > q;
	q.push(make_pair(-dp[u],u));
	while(!q.empty()){
    
    
		pair<int,int> f = q.top();
		q.pop();
		int u = f.second;
		if(vis[u]){
    
    
			continue;
		}
		vis[u] = true;
		for(int i=0;i<G[u].size();i++){
    
    
			int v = G[u][i].v;
			int w = G[u][i].dis;
			if(!vis[v] && dp[u]+w<dp[v]){
    
    
				dp[v] = dp[u] + w;
				q.push(make_pair(-dp[v],v));
			}
		}
	}
}
int main(){
    
    
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
    
    
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		temp.v = v;
		temp.dis = w;
		G[u].push_back(temp);
		temp.v = u;
		G[v].push_back(temp);
	}
	dijstra(1);
	cout << dp[n] << endl;
	return 0;
} 

P1364 医院设置

算法标签: 图论 + Dijkstra + 枚举
注意点: 枚举每个点,假设其为最可能设置的点,统计带权路径长度,取最小值后输出

#include<bits/stdc++.h>
using namespace std;
int n;
struct node{
    
    
	int v;
	int dis;
};
node temp;
vector<node> G[105];
int dp[105];
bool vis[105];
int a[105];
int res = INT_MAX;
void dijstra(int u){
    
    
	memset(dp,0x3f,sizeof(dp));
	memset(vis,0,sizeof(vis));
	dp[u] = 0;
	priority_queue<pair<int,int> > q;
	q.push(make_pair(-dp[u],u));
	while(!q.empty()){
    
    
		pair<int,int> f = q.top();
		q.pop();
		int u = f.second;
		if(vis[u]){
    
    
			continue;
		}
		vis[u] = true;
		for(int i=0;i<G[u].size();i++){
    
    
			int v = G[u][i].v;
			int w = G[u][i].dis;
			if(!vis[v] && dp[u]+w<dp[v]){
    
    
				dp[v] = dp[u] + w;
				q.push(make_pair(-dp[v],v));
			}
		}
	}
	int ans = 0;
	for(int i=1;i<=n;i++){
    
    
		ans += a[i] * dp[i];
	}
	res = min(res,ans); 
}
int main(){
    
    
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
    
    
		int w,l,r;
		scanf("%d%d%d",&w,&l,&r);
		temp.dis = 1;
		a[i] = w;
		if(l){
    
    
			temp.v = l;
			G[i].push_back(temp); 
			temp.v = i;
			G[l].push_back(temp);	
		}
		if(r){
    
    
			temp.v = r;
			G[i].push_back(temp);
			temp.v = i;
			G[r].push_back(temp); 	
		}
	}
	for(int i=1;i<=n;i++){
    
    
		dijstra(i);
	}
	cout << res << endl;
	return 0;
} 

P1529 [USACO2.4]回家 Bessie Come Home

算法标签: 图论 + Dijkstra
注意点: Dijkstra模板题!

#include<bits/stdc++.h>
using namespace std;

int P;
int head[1005];
int cnt = 0;
int dis[1005];

struct Edge{
    
    
	int to;
	int value;
	int next;
};
Edge e[100100];

struct vertex{
    
    
	int v;
	int dis;
	vertex(){
    
    
		
	}
	vertex(int v1,int dis1){
    
    
		v = v1;
		dis = dis1;
	}
	bool operator < (const vertex &x)const{
    
    
		return x.dis < dis;
	}
};
priority_queue<vertex> q;

void addedge(int a,int b,int v){
    
    
	cnt++;
	e[cnt].to = b;
	e[cnt].value = v;
	e[cnt].next = head[a];
	head[a] = cnt;
	return;
}

int main(){
    
    
	scanf("%d",&P);
	for(int i=0;i<P;i++){
    
    
		char u[2];
		char v[2];
		int w;
		int u1;
		int v1;
		scanf("%s%s%d",u,v,&w);
		if(isupper(u[0])){
    
    
			u1 = u[0]-'A' + 1;
		}else{
    
    
			u1 = u[0] -'a' + 27;
		}
		if(isupper(v[0])){
    
    
			v1 = v[0] - 'A' + 1;
		}else{
    
    
			v1 = v[0] - 'a' + 27;
		}
		addedge(u1,v1,w);
		addedge(v1,u1,w); 
	}	
	memset(dis,0x3f,sizeof(dis));
	dis[26] = 0;
	q.push(vertex(26,0));
	vertex t;
	while(!q.empty()){
    
    
		t = q.top();
		q.pop();
		for(int i=head[t.v];i!=0;i=e[i].next){
    
    
			if(dis[t.v]+e[i].value<dis[e[i].to]){
    
    
				dis[e[i].to] = dis[t.v] + e[i].value;
				q.push(vertex(e[i].to,dis[e[i].to]));
			}
		} 
	}
	int ans = INT_MAX;
	int site;
	for(int i=1;i<=25;i++){
    
    
		if(dis[i]<ans){
    
    
			ans = dis[i];
			site = i;
		}
	}
	if(site>=1 && site<=26){
    
    
		printf("%c %d\n",site+'A'-1,ans);
	}else{
    
    
		printf("%c %d\n",site+'a'-27,ans);
	}
	
	return 0;
}

P1629 邮递员送信

算法标签: 图论 + Dijkstra
注意点: Dijkstra模板题!

#include<bits/stdc++.h>
using namespace std;
const int INF = 2147483647;
const int maxn = 1e5 + 5;
int dis[maxn];
bool vis[maxn];
vector<pair<int,int > > Gz[maxn],Gf[maxn];
priority_queue<pair<int,int> > q; 
int n,m;
inline int read() {
    
    
	int f = 1, val = 0;
	char ch = getchar();
	while((ch < '0' || ch > '9') && (ch!='-')) {
    
    
		ch = getchar();
	}
	if(ch == '-') {
    
    
		f = -1;
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9') {
    
    
		val = (val << 3) + (val << 1) +(ch - '0');
		ch = getchar();
	}
	return val * f;
}
inline void dijstra1(int s){
    
    
	for(int i=1;i<=n;i++){
    
    
		dis[i] = INF;
		vis[i] = false;
	}
	dis[s] = 0;
	q.push(make_pair(-dis[s],s));
	while(!q.empty()){
    
    
		pair<int,int> x = q.top();
		q.pop();
		if(vis[x.second]){
    
    
			continue;
		}
		int u = x.second;
		vis[u] = true;
		for(int j=0;j<Gf[u].size();j++){
    
    
			int v = Gf[u][j].first;
			if(dis[v]>dis[u] +Gf[u][j].second){
    
    
				dis[v] = dis[u] + Gf[u][j].second;
				q.push(make_pair(-dis[v],v));
			}
		}
	}
}

inline void dijstra2(int s){
    
    
	for(int i=1;i<=n;i++){
    
    
		dis[i] = INF;
		vis[i] = false;
	}
	dis[s] = 0;
	q.push(make_pair(-dis[s],s));
	while(!q.empty()){
    
    
		pair<int,int> x = q.top();
		q.pop();
		if(vis[x.second]){
    
    
			continue;
		}
		int u = x.second;
		vis[u] = true;
		for(int j=0;j<Gz[u].size();j++){
    
    
			int v = Gz[u][j].first;
			if(dis[v]>dis[u] +Gz[u][j].second){
    
    
				dis[v] = dis[u] + Gz[u][j].second;
				q.push(make_pair(-dis[v],v));
			}
		}
	}
	
}
int main() {
    
    
	n = read();
	m = read();
	for(register int i=0; i<m; i++) {
    
    
		int u =read();
		int v =read();
		int w =read();
		Gz[u].push_back(make_pair(v,w));
		Gf[v].push_back(make_pair(u,w));
	}
	dijstra2(1);
	long long sum = 0;
	for(int i=2;i<=n;i++){
    
    
		sum += dis[i];
	}
	dijstra1(1);
	for(int i=2;i<=n;i++){
    
    
		sum += dis[i];
	}
	cout << sum << endl;
	return 0;
}

P2888 [USACO07NOV]Cow Hurdles S

算法标签: 图论 + Floyd
注意点: Floyd模板题!

#include<bits/stdc++.h>
using namespace std;
int N,M,T;
int dis[305][305];

int main(){
    
    
	scanf("%d%d%d",&N,&M,&T);
	memset(dis,0x3f,sizeof(dis));
	
	for(int i=0;i<M;i++){
    
    
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		dis[u][v] = w;
	}
	for(int k=1;k<=N;k++){
    
    
		for(int i=1;i<=N;i++){
    
    
			for(int j=1;j<=N;j++){
    
    
				if((i!=j) && (j!=k) && (k!=i)){
    
    
					dis[i][j] = min(max(dis[i][k],dis[k][j]),dis[i][j]);
				}
			}
		}
	}
	for(int i=0;i<T;i++){
    
    
		int u,v;
		scanf("%d%d",&u,&v);
		if(dis[u][v]==0x3f3f3f3f){
    
    
			printf("-1\n");
		}else{
    
    
			printf("%d\n",dis[u][v]);	
		}
	}
	
	return 0;
}

P4779 【模板】单源最短路径(标准版)

算法标签: 图论 + Dijkstra
注意点: Dijkstra模板题!

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2147483647;
struct node{
    
    
	int v;
	int dis;
};
node temp;

vector<node> G[100005];
int dp[100005];
bool vis[100005];
priority_queue<pair<int,int> > q;

void dijkstra(int s){
    
    
	dp[s] = 0;
	q.push(make_pair(-dp[s],s));
	while(!q.empty()){
    
    
		pair<int,int> f = q.top();
		q.pop();
		int v = f.second;
		int dis = abs(f.first);
		if(vis[v]){
    
    
			continue;
		}
		vis[v] = true;
		dp[v] = dis;
		for(int i=0;i<G[v].size();i++){
    
    
			node t = G[v][i];
			int u = t.v;
			int d = t.dis;
			if(dp[v]+d<dp[u]){
    
    
				dp[u] = dp[v] + d;
				q.push(make_pair(-dp[u],u));
			}
		}
	}
}

int main(){
    
    
	memset(dp,0x3f,sizeof(dp));
	int n,m,s;
	cin >> n >> m >> s;
	for(int i=1;i<=m;i++){
    
    
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		temp.v = v;
		temp.dis = w;
		G[u].push_back(temp);
	}
	dijkstra(s);
	for(int i=1;i<=n;i++){
    
    
		printf("%d ",dp[i]);
	}
	return 0;
}

P2951 [USACO09OPEN]Hide and Seek S

算法标签: 图论 + Dijkstra
注意点: Dijkstra模板题!

#include<bits/stdc++.h>
using namespace std;

int n,m;
int head[20005];
int cnt = 0;
int dis[20005];
bool vis[20005];
struct Edge{
    
    
	int value;
	int to;
	int next;
};
Edge e[200005];

void addedge(int a,int b,int v){
    
    
	cnt++;
	e[cnt].value = v;
	e[cnt].to = b;
	e[cnt].next = head[a];
	head[a] = cnt;
	return;	
}

void SPFA(){
    
    
	int u;
	queue<int> q;
	dis[1] = 0;
	q.push(1);
	vis[1] = true;
	
	while(!q.empty()){
    
    
		u = q.front();
		vis[u] = false;
		q.pop();
		for(int i=head[u];i;i=e[i].next){
    
    
			if(dis[u] + e[i].value < dis[e[i].to]){
    
    
				dis[e[i].to] = dis[u] + e[i].value;
				if(!vis[e[i].to]){
    
    
					vis[e[i].to] = true;
					q.push(e[i].to);
				}
			} 
		}
	}
}

int main(){
    
    
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
    
    
		int u,v;
		scanf("%d%d",&u,&v);
		addedge(u,v,1);
		addedge(v,u,1);
	}
	memset(dis,0x3f,sizeof(dis));
	SPFA();
	int ans = INT_MIN;
	int site = -1;
	for(int i=1;i<=n;i++){
    
    
		if(dis[i]!=0x3f3f3f3f){
    
    
			if(dis[i]>ans){
    
    
				ans = dis[i];
				site = i;
			}
		}
	}
	int cnt = 0;
	for(int i=1;i<=n;i++){
    
    
		if(ans == dis[i]){
    
    
			cnt++;
		}
	}
	printf("%d %d %d\n",site,ans,cnt);
	return 0;
}

P3905 道路重建

算法标签: 图论 + Dijkstra
注意点: Dijkstra模板题!

#include<bits/stdc++.h>
using namespace std;
int n,m;
int G[105][105];
struct Edge{
    
    
	int to;
	int value;
	int next;
}; 
Edge e[20005];
int cnt = 0;
int head[105];
int dis[105];
bool vis[105];
int k;
int A,B;

void addedge(int a,int b,int v){
    
    
	cnt++;
	e[cnt].to = b;
	e[cnt].value = v;
	e[cnt].next = head[a];
	head[a] = cnt;
	return;
}

void SPFA(){
    
    
	int u;
	dis[A] = 0;
	vis[A] = true;
	queue<int> q;
	q.push(A);
	
	while(!q.empty()){
    
    
		u = q.front();
		q.pop();
		vis[u] = false;
		for(int i=head[u];i;i=e[i].next){
    
    
			if(dis[u] + e[i].value < dis[e[i].to]){
    
    
				dis[e[i].to] = dis[u] + e[i].value;
				if(!vis[e[i].to]){
    
    
					vis[e[i].to] = true;
					q.push(e[i].to);
				}
			}
		}
	}
}

int main(){
    
    
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
    
    
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		G[u][v] = G[v][u] = w;
		addedge(u,v,0);
		addedge(v,u,0);
	}
	scanf("%d",&k);
	for(int i=1;i<=k;i++){
    
    
		int u,v;
		scanf("%d%d",&u,&v);
		for(int j=head[u];j;j=e[j].next){
    
    
			if(e[j].to == v){
    
    
				e[j].value = G[u][v];
			}
		}
		for(int j=head[v];j;j=e[j].next){
    
    
			if(e[j].to == u){
    
    
				e[j].value = G[u][v];
			}
		}
	}
	scanf("%d%d",&A,&B);
	memset(dis,0x3f,sizeof(dis));
	SPFA();
	printf("%d\n",dis[B]);
	
	return 0;
}

P1690 贪婪的Copy

算法标签: 图论 + Floyd + DFS
注意点: 由于题目中给出的点的数量为N(<100),因此可考虑floyd全源最短路径的方法,求出所有点之间的最短路,由于需要枚举的点数量P(<10),因此可采用DFS搜索的方法,枚举所有可能,求得最短路长度后输出!

#include<bits/stdc++.h>
using namespace std;
int N;
int P;
int G[105][105];
int a[15];
bool vis[15];
int ans = INT_MAX;

void dfs(int u,int sum,int depth){
    
    
	if(depth == P){
    
    
		ans = min(ans,sum+G[u][N]);
		return;
	}
	for(int i=1;i<=P;i++){
    
    
		if(!vis[i]){
    
    
			vis[i] = true;
			dfs(a[i],sum+G[u][a[i]],depth+1);
			vis[i] = false;
		}
	}
}
int main(){
    
    
	scanf("%d",&N);
	for(int i=1;i<=N;i++){
    
    
		for(int j=1;j<=N;j++){
    
    
			scanf("%d",&G[i][j]);
		}
	}	

	scanf("%d",&P);
	for(int i=1;i<=P;i++){
    
    
		scanf("%d",&a[i]);
	}
	for(int k=1;k<=N;k++){
    
    
		for(int i=1;i<=N;i++){
    
    
			for(int j=1;j<=N;j++){
    
    
				G[i][j] = min(G[i][j],G[i][k]+G[k][j]);
			}
		}
	}
	
	dfs(1,0,0); 
	cout << ans << endl;
	return 0;
}

P6833 [Cnoi2020]雷雨

算法标签: 图论 + Dijkstra + 枚举
注意点: 一道好题!(本题真实难度应该是绿色)
首先是解题思路,我们需要构造出一幅图,把每个格子看成顶点,将格子相邻的连接看作边,而边权对应着到达点的点权,初始时我们从一个点出发,将最短路长度更新为起点的点权,然后做dijkstra算法,那么需要做几次呢?由于题目中有3个初始的位置,可把路分成3段来计算,因此需要3次dijkstra算法,依次更新得到雷电、森林和木屋到格子中任何一个点的距离,目标函数为 d p 1 [ i ] + d p 2 [ i ] + d p 3 [ i ] − 2 ∗ a [ x ] [ y ] , ( x − 1 ) ∗ m + y = i dp1[i]+dp2[i]+dp3[i]-2*a[x][y],(x-1)*m+y=i dp1[i]+dp2[i]+dp3[i]2a[x][y],(x1)m+y=i(枚举的点权加了3遍,因此-2),我们枚举图中所有的点,求其距离和最小值即可
最后注意,不开long long一场空!100->30

#include<bits/stdc++.h>
using namespace std;
int n,m,a,b,c;
int aa[1005][1005];
typedef long long LL;
struct node{
    
    
	int v;
	int w;
};
node temp;
const int maxn = 1e6+5;
vector<node> G[maxn];
bool vis[maxn];
LL dp1[maxn];
LL dp2[maxn];
LL dp3[maxn];

void dijkstra(int u,LL dp[],int x,int y){
    
    
	memset(vis,0,sizeof(vis));
	dp[u] = aa[x][y];
	priority_queue<pair<LL,int> > q;
	q.push(make_pair(-dp[u],u));
	while(!q.empty()){
    
    
		pair<LL,int> f = q.top();
		q.pop();
		int u = f.second;
		if(vis[u]){
    
    
			continue;
		}
		vis[u] = true;
		for(int i=0;i<G[u].size();i++){
    
    
			int v = G[u][i].v;
			int w = G[u][i].w;
			if(!vis[v] && dp[u] + w < dp[v]){
    
    
				dp[v] = dp[u] + w;
				q.push(make_pair(-dp[v],v));
			}
		}
	}
}

int main(){
    
    
	memset(dp1,0x3f,sizeof(dp1));
	memset(dp2,0x3f,sizeof(dp2));
	memset(dp3,0x3f,sizeof(dp3));
	scanf("%d%d%d%d%d",&n,&m,&a,&b,&c);
	for(int i=1;i<=n;i++){
    
    
		for(int j=1;j<=m;j++){
    
    
			scanf("%d",&aa[i][j]);
		}
	}
	for(int i=1;i<=n;i++){
    
    
		for(int j=1;j<=m;j++){
    
    
			if(i!=n && j!=m){
    
    
				int t1 = (i-1)*m + j;
				int t2 = (i-1)*m + (j+1);
				temp.v = t2;
				temp.w = aa[i][j+1];
				G[t1].push_back(temp);
				temp.v = t1;
				temp.w = aa[i][j];
				G[t2].push_back(temp);
				int t3 = i*m + j;
				temp.v = t3;
				temp.w = aa[i+1][j];
				G[t1].push_back(temp);
				temp.v = t1;
				temp.w = aa[i][j];
				G[t3].push_back(temp);
			}else if(i==n && j!=m){
    
    
				int t1 = (i-1)*m + j;
				int t2 = (i-1)*m + (j+1);
				temp.v = t2;
				temp.w = aa[i][j+1];
				G[t1].push_back(temp);
				temp.v = t1;
				temp.w = aa[i][j];
				G[t2].push_back(temp);
			}else if(i!=n && j==m){
    
    
				int t1 = (i-1)*m + j;
				int t3 = i*m + j;
				temp.v = t3;
				temp.w = aa[i+1][j];
				G[t1].push_back(temp);
				temp.v = t1;
				temp.w = aa[i][j];
				G[t3].push_back(temp);
			}
		}
	}
	dijkstra(a,dp1,1,a);
	dijkstra((n-1)*m+b,dp2,n,b);
	dijkstra((n-1)*m+c,dp3,n,c);
	long long ans = LONG_LONG_MAX;
	for(int i=1;i<=n;i++){
    
    
		for(int j=1;j<=m;j++){
    
    
			long long tmp = (long long)dp1[(i-1)*m+j] 
			+ (long long)dp2[(i-1)*m+j] 
			+ (long long)dp3[(i-1)*m+j] 
			- 2LL*aa[i][j];
			ans = min(ans,tmp);
		}
	}
	cout << ans << endl;
	return 0;
} 

P6770 [USACO05MAR]Checking an Alibi 不在场的证明

算法标签: 图论 + Dijkstra
注意点: Dijkstra模板题

#include<bits/stdc++.h>
using namespace std;
int F,P,C,M;
struct node{
    
    
	int v;
	int w;
};
node temp;
vector<node> G[505];
int dp[505];
int a[105];
bool vis[505];
priority_queue<pair<int,int> > q;
void dijkstra(int u){
    
    
	dp[u] = 0;
	q.push(make_pair(-0,u));
	while(!q.empty()){
    
    
		pair<int,int> f = q.top();
		q.pop();
		int u = f.second;
		if(vis[u]){
    
    
			continue;
		}
		vis[u] = true;
		for(int i=0;i<G[u].size();i++){
    
    
			int v = G[u][i].v;
			int dis = G[u][i].w;
			if(!vis[v] && dis+dp[u]<dp[v]){
    
    
				dp[v] = dp[u] + dis;
				
			}q.push(make_pair(-dp[v],v)); 
		}
	}
}
int main(){
    
    
	memset(dp,0x3f,sizeof(dp));
	scanf("%d%d%d%d",&F,&P,&C,&M);
	for(int i=1;i<=P;i++){
    
    
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		temp.v = v;
		temp.w = w;
		G[u].push_back(temp);
		temp.v = u;
		G[v].push_back(temp);
	}
	for(int i=1;i<=C;i++){
    
    
		scanf("%d",&a[i]);
	}
	dijkstra(1);
	int ans = 0;
	for(int i=1;i<=C;i++){
    
    
		if(dp[a[i]]<=M){
    
    
			ans++;
		}
	}
	cout << ans << endl;
	for(int i=1;i<=C;i++){
    
    
		if(dp[a[i]]<=M){
    
    
			printf("%d\n",i);
		}
	}
	return 0;
}

P1342 请柬

算法标签: 图论 + Dijkstra
注意点: Dijkstra模板题,注意这里用的是链式前向星存储图,上一题模板采用的是邻接表!

#include<bits/stdc++.h>
using namespace std;
struct Edge{
    
    
	long long value;
	int to;
	int next;
};
const int maxn = 1e6+10;
int n,m;

int cnt1 = 0;
int cnt2 = 0;
Edge e1[maxn];
Edge e2[maxn];
int head1[maxn];
int head2[maxn];
bool vis1[maxn];
bool vis2[maxn];
long long dis1[maxn];
long long dis2[maxn];

struct vertex{
    
    
	long long dis;
	int v;
	vertex(){
    
    
		
	}
	vertex(int v1,long long d){
    
    
		v = v1;
		dis = d;
	}
	bool operator < (const vertex &x)const{
    
    
		return x.dis < dis;
	}
};
priority_queue<vertex> q1;
priority_queue<vertex> q2;

void addedge1(int a,int b,long long v){
    
    
	cnt1++;
	e1[cnt1].value = v;
	e1[cnt1].to = b;
	e1[cnt1].next = head1[a];
	head1[a] = cnt1;
	return;
}

void addedge2(int b,int a,long long v){
    
    
	cnt2++;
	e2[cnt2].value = v;
	e2[cnt2].to = b;
	e2[cnt2].next = head2[a];
	head2[a] = cnt2;
	return;
}
int main(){
    
    
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
    
    
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		addedge1(u,v,w);
		addedge2(u,v,w);
	}
	memset(dis1,0x3f,sizeof(dis1));
	memset(dis2,0x3f,sizeof(dis2));
	dis1[1] = 0;
	dis2[1] = 0;
	q1.push(vertex(1,0));
	q2.push(vertex(1,0));
	vertex t;
	long long ans = 0;
	while(!q1.empty()){
    
    
		t = q1.top();
		q1.pop();
		if(!vis1[t.v]){
    
    
			vis1[t.v] = true;
			for(int i=head1[t.v];i!=0;i=e1[i].next){
    
    
				if(dis1[t.v] + e1[i].value < dis1[e1[i].to]){
    
    
					dis1[e1[i].to] = dis1[t.v] + e1[i].value;
					q1.push(vertex(e1[i].to,dis1[e1[i].to]));
				}
			}
		}
	}
	while(!q2.empty()){
    
    
		t = q2.top();
		q2.pop();
		if(!vis2[t.v]){
    
    
			vis2[t.v] = true;
			for(int i=head2[t.v];i!=0;i=e2[i].next){
    
    
				if(dis2[t.v] + e2[i].value < dis2[e2[i].to]){
    
    
					dis2[e2[i].to] = dis2[t.v] + e2[i].value;
					q2.push(vertex(e2[i].to,dis2[e2[i].to]));
				}
			}
		}
	}
	for(int i=2;i<=n;i++){
    
    
		ans += dis1[i];
	}
	for(int i=2;i<=n;i++){
    
    
		ans += dis2[i];
	}
	printf("%lld\n",ans);
	return 0;
} 

P2648 赚钱

算法标签: 图论 + Floyd + 最小正环
注意点: 做两遍Floyd算法,如果第二遍最大值距离依然更新,说明存在一个正环,若无更新,则输出最大值点即可,下一题和本题重题

#include<bits/stdc++.h>
using namespace std;
int f[305][305];
int D,P,C,F;
void Floyd(){
    
    
	for(int k=1;k<=C;k++){
    
    
		for(int i=1;i<=C;i++){
    
    
			for(int j=1;j<=C;j++){
    
    
				f[i][j]=max(f[i][j],f[i][k]+f[k][j]);
			}
		}
	}
}
int main(){
    
    

	cin >> D >> P >> C >>F;
	memset(f,0xc0c0c0c0,sizeof(f));
	
	for(int i=0;i<P;i++){
    
    
		int x,y;
		cin >> x >> y;
		f[x][y]=D;
	}
	
	for(int i=0;i<F;i++){
    
    
		int x,y,t;
		cin >> x >> y >> t;
		f[x][y] = D-t;
	}
	
	Floyd();
	int maxn1=0xc0c0c0c0;
	int maxn2=0xc0c0c0c0;
	for(int i=1;i<=C;i++){
    
    
		for(int j=1;j<=C;j++){
    
    
			maxn1 = max(maxn1,f[i][j]);
		}
	}
	Floyd();
	for(int i=1;i<=C;i++){
    
    
		for(int j=1;j<=C;j++){
    
    
			maxn2 = max(maxn2,f[i][j]); 
		}
	}
	if(maxn1!=maxn2){
    
    
		cout <<"orz"<<endl;
	}else{
    
    
		cout << D+maxn1 <<endl;
	}
	return 0;
}

P1938 [USACO09NOV]Job Hunt S

算法标签: 图论 + Floyd + 最小正环
注意点: 和上一题P2648重题

#include<bits/stdc++.h>
using namespace std;
int f[305][305];
int D,P,C,F,S;
void Floyd(){
    
    
	for(int k=1;k<=C;k++){
    
    
		for(int i=1;i<=C;i++){
    
    
			for(int j=1;j<=C;j++){
    
    
				f[i][j]=max(f[i][j],f[i][k]+f[k][j]);
			}
		}
	}
}
int main(){
    
    
	cin >> D >> P >> C >>F >> S;
	memset(f,0xc0c0c0c0,sizeof(f));
	
	for(int i=0;i<P;i++){
    
    
		int x,y;
		cin >> x >> y;
		f[x][y]=D;
	}
	
	for(int i=0;i<F;i++){
    
    
		int x,y,t;
		cin >> x >> y >> t;
		f[x][y] = D-t;
	}
	
	Floyd();
	int maxn1=0xc0c0c0c0;
	int maxn2=0xc0c0c0c0;
	for(int i=1;i<=C;i++){
    
    
		for(int j=1;j<=C;j++){
    
    
			maxn1 = max(maxn1,f[i][j]);
		}
	}
	Floyd();
	for(int i=1;i<=C;i++){
    
    
		for(int j=1;j<=C;j++){
    
    
			maxn2 = max(maxn2,f[i][j]); 
		}
	}
	if(maxn1!=maxn2){
    
    
		cout << "-1" <<endl;
	}else{
    
    
		maxn1=0xc0c0c0c0;
		for(int i=1;i<=C;i++){
    
    
			maxn1 = max(maxn1,f[S][i]);	
		}
		cout << D+maxn1 <<endl;
	}
	return 0;
}

P3371 【模板】单源最短路径(弱化版)

算法标签: 图论 + Dijkstra
注意点: Dijkstra模板题!

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2147483647;
struct node{
    
    
	int v;
	int dis;
};
node temp;

vector<node> G[10005];
int dp[10005];
bool vis[10005];
priority_queue<pair<int,int> > q;

void dijkstra(int s){
    
    
	dp[s] = 0;
	q.push(make_pair(-dp[s],s));
	while(!q.empty()){
    
    
		pair<int,int> f = q.top();
		q.pop();
		int v = f.second;
		int dis = abs(f.first);
		if(vis[v]){
    
    
			continue;
		}
		vis[v] = true;
		dp[v] = dis;
		for(int i=0;i<G[v].size();i++){
    
    
			node t = G[v][i];
			int u = t.v;
			int d = t.dis;
			if(dp[v]+d<dp[u]){
    
    
				dp[u] = dp[v] + d;
				q.push(make_pair(-dp[u],u));
			}
		}
	}
}

int main(){
    
    
	fill(dp,dp+10005,maxn);
	int n,m,s;
	cin >> n >> m >> s;
	for(int i=1;i<=m;i++){
    
    
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		temp.v = v;
		temp.dis = w;
		G[u].push_back(temp);
	}
	dijkstra(s);
	for(int i=1;i<=n;i++){
    
    
		printf("%d ",dp[i]);
	}
	return 0;
}

P6464 传送门

算法标签: 图论 +Floyd
注意点: 这道题需要枚举每两对顶点,假设其路径连接为0,用这两个点去更新所有现有的最短路径长度,然后统计一个最小结果即可!

#include<bits/stdc++.h>
using namespace std;
int N,m;
int G[105][105];
int G2[105][105];

const int INF = 0x3f3f3f3f;
int main(){
    
    
	scanf("%d%d",&N,&m);
	memset(G,INF,sizeof(G));
	for(int i=1;i<=N;i++){
    
    
		G[i][i] = 0;
	}
	for(int i=1;i<=m;i++){
    
    
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		G[u][v] = G[v][u] = w;
	}
	for(int k=1;k<=N;k++){
    
    
		for(int i=1;i<=N;i++){
    
    
			for(int j=1;j<=N;j++){
    
    
				G[i][j] = min(G[i][j],G[i][k]+G[k][j]);
			}
		}
	}
	int res = INT_MAX;
	for(int p=1;p<=N-1;p++){
    
    
		for(int q=p+1;q<=N;q++){
    
    
			for(int i=1;i<=N;i++){
    
    
				for(int j=1;j<=N;j++){
    
    
					G2[i][j] = G[i][j];
				}
			}
			G2[p][q] = G2[q][p] = 0;
			for(int i=1;i<=N;i++){
    
    
				for(int j=1;j<=N;j++){
    
    
					G2[i][j] = min(G2[i][j],G2[i][p]+G2[p][j]);
				}
			}
			for(int i=1;i<=N;i++){
    
    
				for(int j=1;j<=N;j++){
    
    
					G2[i][j] = min(G2[i][j],G2[i][q]+G2[q][j]);
				}
			}
			int ans = 0;
			for(int i=1;i<=N-1;i++){
    
    
				for(int j=i+1;j<=N;j++){
    
    
					ans += G2[i][j];
				}
			}
			res = min(res,ans);
		}
	}
	cout << res << endl;
	return 0;
}

P3906 Geodetic集合

算法标签: 图论 + Dijkstra + DFS
注意点: Dijkstra + DFS模板题!

#include<bits/stdc++.h>
using namespace std;
int n,m;
int k;
struct node{
    
    
	int v;
	int w;
};
node temp;
vector<node> G[45];
vector<int> pre[45];
set<int> s;
int dp[45];
bool vis[45];
void dijkstra(int u){
    
    
	memset(vis,0,sizeof(vis));
	memset(dp,0x3f,sizeof(dp));
	dp[u] = 0;
	priority_queue<pair<int,int> > q;
	q.push(make_pair(-dp[u],u));
	while(!q.empty()){
    
    
		pair<int,int> f = q.top();
		q.pop();
		int u = f.second;
		if(vis[u]){
    
    
			continue;
		}
		vis[u] = true;
		for(int i=0;i<G[u].size();i++){
    
    
			int v = G[u][i].v;
			int w = G[u][i].w;
			if(!vis[v] && dp[u] + w < dp[v]){
    
    
				pre[v].clear();
				pre[v].push_back(u);
				dp[v] = dp[u] + w;
				q.push(make_pair(-dp[v],v));
			}else if(!vis[v] && dp[u] + w == dp[v]){
    
    
				pre[v].push_back(u);
			}
		}
	}
}
void dfs(int u,int v){
    
    
	if(v == u){
    
    
		s.insert(u);
		return;
	}
	s.insert(v);
	for(int i=0;i<pre[v].size();i++){
    
    
		dfs(u,pre[v][i]);
	}
}
int main(){
    
    
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
    
    
		int u,v;
		scanf("%d%d",&u,&v);
		temp.v = v;
		temp.w = 1;
		G[u].push_back(temp);
		temp.v = u;
		G[v].push_back(temp);
	}
	scanf("%d",&k);
	for(int i=1;i<=k;i++){
    
    
		int u,v;
		for(int j=1;j<=n;j++){
    
    
			pre[j].clear();
		}
		s.clear();
		scanf("%d%d",&u,&v);
		dijkstra(u);
		dfs(u,v);
		for(auto it=s.begin();it!=s.end();it++){
    
    
			printf("%d ",*it);
		}
		printf("\n");
	}
	return 0;
} 

P1807 最长路

算法标签: 图论 + Dijkstra
注意点: Dijkstra模板题!

#include<bits/stdc++.h>
using namespace std;
int n,m;
struct Edge{
    
    
	int to;
	int next;
	int val;
};
Edge e[100005];
int head[1505];
int dis[1505];
int cnt = 0;
int vis[1505];

void addedge(int a,int b,int v){
    
    
	cnt++;
	e[cnt].to = b;
	e[cnt].val = v;
	e[cnt].next = head[a];
	head[a] = cnt;
	return;
}
void SPFA(){
    
    
	int u;
	dis[1] = 0;
	vis[1] = true;
	queue<int> q;
	q.push(1);
	while(!q.empty()){
    
    
		u = q.front();
		q.pop();
		vis[u] = false;
		for(int i=head[u];i;i=e[i].next){
    
    
			if(dis[u] + e[i].val < dis[e[i].to]){
    
    
				dis[e[i].to] = dis[u] + e[i].val;
				if(!vis[e[i].to]){
    
    
					vis[e[i].to] = true;
					q.push(e[i].to);
				}
			}
		}
	} 
}
int main(){
    
    
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
    
    
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		addedge(u,v,-w);
	}
	memset(dis,0x3f,sizeof(dis));
	SPFA();
	if(dis[n]==0x3f3f3f3f){
    
    
		printf("-1\n");
	}else{
    
    
		printf("%d\n",abs(dis[n]));
	}
	return 0;
}

P3385 【模板】负环

算法标签: 图论 + Bellman-Ford
注意点: Bellman-Ford模板题!
注意题目判断的是从1出发可达的负环,若从1出发不可达,则不必松弛该顶点

#include<bits/stdc++.h>
using namespace std;

int n,m;
struct node{
    
    
	int u;
	int v;
	int w;
};
node t[6005];

int dp[2005];
int cnt = 0;

bool Bellman(int u){
    
    
	memset(dp,0x3f,sizeof(dp));
	dp[u] = 0;
	for(int i=1;i<=n-1;i++){
    
    
		for(int j=0;j<cnt;j++){
    
    
			int u = t[j].u;
			int v = t[j].v;
			int dis = t[j].w;
			if(dp[u]!=0x3f3f3f3f && dp[u] + dis < dp[v]){
    
    
				dp[v] = dp[u] + dis;
			}
		}
	}
	for(int j=0;j<cnt;j++){
    
    
		int u = t[j].u;
		int v = t[j].v;
		int dis = t[j].w;
		if(dp[u]!=0x3f3f3f3f && dp[u] + dis < dp[v]){
    
    
			return true;
		}
	}
	return false;
}
int main(){
    
    
	int T;
	scanf("%d",&T);
	while(T--){
    
    
		scanf("%d%d",&n,&m);
		cnt = 0;
		for(int i=1;i<=m;i++){
    
    
			int u,v,w;
			scanf("%d%d%d",&u,&v,&w);
			t[cnt].u = u;
			t[cnt].v = v;
			t[cnt++].w = w;
			if(w>=0){
    
    
				t[cnt].u = v;
				t[cnt].v = u;
				t[cnt++].w = w;
			}
		}
		bool f = Bellman(1);
		if(f){
    
    
			printf("YES\n");
		}else{
    
    
			printf("NO\n");
		}
	}
	return 0;
} 

P2136 拉近距离

算法标签: 图论 + SPFA
注意点: SPFA模板题!接上一题,本题核心思想也是判断负环,此处用的是改进的Bellman-Ford算法,即SPFA算法

#include<bits/stdc++.h>
using namespace std;

int N,M;
struct Edge{
    
    
	int to;
	int next;
	int value;
};

Edge e[20005];
int head[1005];
int dis[1005];
int dis1[1005];
int dis2[1005];
bool vis[1005];
int cnt = 0;
int ans[1005];

void addedge(int a,int b,int v){
    
    
	cnt++;
	e[cnt].value = v;
	e[cnt].to = b;
	e[cnt].next = head[a];
	head[a] = cnt;
	return;
}

bool SPFA(int s){
    
    
	memset(dis,0x3f,sizeof(dis));
	memset(vis,0,sizeof(vis));
	memset(ans,0,sizeof(ans));
	int u;
	dis[s] = 0;
	vis[s] = true;
	queue<int> q;
	q.push(s);
	while(!q.empty()){
    
    
		u = q.front();
		q.pop();
		vis[u] = false;
		for(int i=head[u];i;i=e[i].next){
    
    
			if(dis[u]+e[i].value<dis[e[i].to]){
    
    
				dis[e[i].to] = dis[u] + e[i].value;
				if(!vis[e[i].to]){
    
    
					vis[e[i].to] = true;
					ans[e[i].to]++;
					q.push(e[i].to);
					if(ans[e[i].to]>=N){
    
    
						return true;
					}
				}
			}
		}
	}
	return false;
} 
int main(){
    
    

	scanf("%d%d",&N,&M);
	for(int i=1;i<=M;i++){
    
    
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		addedge(u,v,-w);
	}
	
    bool f1 = SPFA(1);
    for(int i=1;i<=N;i++){
    
    
    	dis1[i] = dis[i];
	}
	
    bool f2 = SPFA(N);
    for(int i=1;i<=N;i++){
    
    
    	dis2[i] = dis[i];
	}
	
	if(f1 || f2){
    
    
		printf("Forever love\n");
	}else{
    
    
		printf("%d\n",min(dis1[N],dis2[1]));
	}
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/weixin_41801682/article/details/123359428