C++ 最短路(distra) 洛谷

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

题目背景

2018 年 7 月 19 日,某位同学在 NOI Day 1 T1 归程 一题里非常熟练地使用了一个广为人知的算法求最短路。

然后呢?

100→60;

Ag→Cu;

最终,他因此没能与理想的大学达成契约。

小 F 衷心祝愿大家不再重蹈覆辙。

题目描述

给定一个 n 个点,m 条有向边的带非负权图,请你计算从 s 出发,到每个点的距离。

数据保证你能从 s 出发到任意点。

输入格式

第一行为三个正整数 n,m,s。 第二行起 mm 行,每行三个非负整数 ui,vi,wi,表示从 ui​ 到 vi 有一条权值为 wi​ 的有向边。

输出格式

输出一行 n 个空格分隔的非负整数,表示 s 到每个点的距离。

#include<bits/stdc++.h>
#define int long long
#define PII pair<int,int>
#define fi first
#define se second
#define pb push_back
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)


using namespace std;

const int N = 1e5+10;

int n,m,T;
int A,B;
int dist[N],vis[N];

vector<PII > e[N];

void distra()
{
	priority_queue<PII,vector<PII>,greater<PII>> q;
	for(int i=1;i<=n;i++) dist[i] = 1e18;
	q.push({0,A});
	dist[A] = 0;
	while(q.size())
	{
		auto t = q.top();q.pop();
		int now = t.se,dis = t.fi;
		if(vis[now]==1) continue;
		vis[now] = 1;
		for(auto tt:e[now])
		{
			int spot = tt.se,w = tt.fi;
			if(dist[spot]>dist[now]+w)
			{
				dist[spot] = dist[now]+w;
				q.push({dist[spot],spot});
			}
		}
	}
}

signed main()
{
	IOS;
	cin>>n>>m>>A;
	while(m--)
	{
		int a,b,c;
		cin>>a>>b>>c;
		e[a].pb({c,b});
	}
	distra();
	for(int i=1;i<=n;i++) cout<<dist[i]<<" ";
	return 0;
}

道路重建

题目描述

从前,在一个王国中,在 n 个城市间有 m 条道路连接,而且任意两个城市之间至多有一条道路直接相连。在经过一次严重的战争之后,有 d 条道路被破坏了。国王想要修复国家的道路系统,现在有两个重要城市 A 和 B 之间的交通中断,国王希望尽快的恢复两个城市之间的连接。你的任务就是修复一些道路使 A 与 B 之间的连接恢复,并要求修复的道路长度最小。

输入格式

输入文件第一行为一个整数 n (2<n≤100),表示城市的个数。这些城市编号从 1 到 n。

第二行为一个整数 m (n−1≤m≤12n(n−1)),表示道路的数目。

接下来的 m 行,每行 3 个整数 i,j,k (1≤i,j≤n,i≠j,0<k≤100),表示城市 i 与 j 之间有一条长为 k 的道路相连。

接下来一行为一个整数 d (1≤d≤m),表示战后被破坏的道路的数目。在接下来的 d 行中,每行两个整数 i 和 j,表示城市 i 与 j 之间直接相连的道路被破坏。

最后一行为两个整数 A 和 B,代表需要恢复交通的两个重要城市。

输出格式

输出文件仅一个整数,表示恢复 A 与 B 间的交通需要修复的道路总长度的最小值。

#include<bits/stdc++.h>
#define int long long
#define PII pair<int,int>
#define se second
#define fi first
#define pb push_back
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);

using namespace std;

const int N = 1e5+10;

vector<PII > e[N];

int n,m,d,A,B;
map<pair<int,int>,int> mp;
int dist[N],vis[N];

void distra()
{
	priority_queue<PII,vector<PII>,greater<PII>> q;
	for(int i = 1;i<=n;i++) dist[i] = 1e18;
	q.push({0,A});
	dist[A] = 0;
	while(q.size())
	{
		auto t = q.top();
		q.pop();
		int now = t.se;
		int dis = t.fi;
		if(vis[now] == 1)continue;
		vis[now] = 1;
		for(auto tt:e[now])
		{
			int spot = tt.se;
			int w = 0;
			if(mp[{now,spot}] == 1) w = tt.fi;
			if(dist[spot]>dist[now]+w)
			{
				dist[spot] = dist[now]+w;
				q.push({dist[spot],spot});
			}
		}
	}
}

signed main()
{
	IOS;
	cin>>n>>m;
	while(m--)
	{
		int a,b,c;
		cin>>a>>b>>c;
		e[a].pb({c,b});
		e[b].pb({c,a});
	}
	cin>>d;
	for(int i=1;i<=d;i++)
	{
		int a,b;
		cin>>a>>b;
		mp[{a,b}] = 1;
		mp[{b,a}] = 1;
	}
	cin>>A>>B;
	distra();
	cout<<dist[B];
	return 0;
}

小明的游戏

题目描述

小明最近喜欢玩一个游戏。给定一个n×m的棋盘,上面有两种格子#@。游戏的规则很简单:给定一个起始位置和一个目标位置,小明每一步能向上,下,左,右四个方向移动一格。如果移动到同一类型的格子,则费用是0,否则费用是1。请编程计算从起始位置移动到目标位置的最小花费。

输入格式

输入文件有多组数据。
输入第一行包含两个整数n,m,分别表示棋盘的行数和列数。
输入接下来的n行,每一行有m个格子(使用#或者@表示)。
输入接下来一行有四个整数x1,y1,x2,y2​,分别为起始位置和目标位置。
当输入n,m均为0时,表示输入结束。

输出格式

对于每组数据,输出从起始位置到目标位置的最小花费。每一组数据独占一行。

#include<bits/stdc++.h>
#define int long long
#define PII pair<int,pair<int,int> >
#define fi first
#define se second
#define pb push_back
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)

using namespace std;

const int N = 505;

int n,m;
char va[N][N];
int dist[N][N],vis[N][N];
int sx,sy,ex,ey;
int dx[4] = {0,0,1,-1};
int dy[4] = {1,-1,0,0};

void distra()
{
	for(int i = 1;i<=n;i++)
	{
		for(int j = 1;j<=m;j++)
		{
			dist[i][j] = 1e18;
			vis[i][j] = 0;
		}
	}
	priority_queue<PII,vector<PII>,greater<PII>> q;
	q.push({0,{sx,sy}});//移动距离,目标坐标 
	dist[sx][sy] = 0;
	while(q.size())
	{
		auto t = q.top();
		q.pop();
		int x = t.se.fi,y = t.se.se;//int x = sx , y = sy; 
		if(vis[x][y]==1) continue;
		vis[x][y] = 1;//标记 
		for(int i = 0;i<=3;i++)//从 1到 4 
		{ 
			int xx = x+dx[i],yy = y+dy[i];//{xx,yy}为当前坐标 
			if(xx>=1&&xx<=n&&yy>=1&&yy<=m)
			{
				int w = (va[x][y]!=va[xx][yy]);
				if(dist[xx][yy]>dist[x][y]+w)
				{
					dist[xx][yy] = dist[x][y]+w;
					q.push({dist[xx][yy],{xx,yy}});
				}
			}
		}
	}
		
}

signed main()
{
	IOS;
	while(1)
	{
		cin>>n>>m;
		if(n==0&&m==0) break;
		for(int i = 1;i<=n;i++)
		{
			for(int j = 1;j<=m;j++)
			{
				cin>>va[i][j];
			}
		
		} 	
		cin>>sx>>sy>>ex>>ey;
		sx+=1,sy+=1,ex+=1,ey+=1;
		distra();
		cout<<dist[ex][ey]<<"\n";
	}
	
	
	return 0;
}

[USACO19DEC] Milk Pumping G

题目描述

Farmer John 最近为了扩张他的牛奶产业帝国而收购了一个新的农场。这一新的农场通过一个管道网络与附近的小镇相连,FJ 想要找出其中最合适的一组管道,将其购买并用来将牛奶从农场输送到小镇。

这个管道网络可以用 N 个接合点(管道的端点)来描述,将其编号为 1…N。接合点 11 表示 FJ 的农场,接合点 N 表示小镇。有 M 条双向的管道,每条连接了两个接合点。使用第 i 条管道需要 FJ 花费 ci​ 美元购入,可以支持每秒 fi​ 升牛奶的流量。

FJ 想要购买一条管道组成一条单一路径,路径的两端点分别为接合点 1 和 N。这条路径的花费等于路径上所有管道的费用之和。路径上的流量等于路径上所有管道的最小流量(因为这是沿这条路径输送牛奶的瓶颈)。FJ 想要最大化路径流量与路径花费之比。保证存在从 1 到 N之间的路径。

输入格式

输入的第一行包含 N 和 M。以下 M 行每行以四个整数描述一条管道:a 和 b(管道连接的两个不同的接合点),c(管道的花费),以及 f(管道的流量)。花费和流量均为范围 1…1000 之内的正整数。

输出格式

输出 10的六次方 乘以最优解的值,并向下取整(也就是说,如果这个数本身不是整数,输出小于它的最接近它的整数)。

#include<bits/stdc++.h>
#define int long long
#define PII pair<int,pair<int,int> >
#define Pii pair<int,int >
#define fi first
#define se second
#define pb push_back
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)

using namespace std;

const int N = 1e5+10;
int a,b,c,f;
int n,m;
int dist[N],vis[N];

vector<PII > e[N];

int check(int mid)
{
	for(int i = 1;i<=n;i++) dist[i] = 1e18,vis[i] = 0;
	priority_queue<Pii,vector<Pii>,greater<Pii>> q;
	q.push({0,1});
	dist[1] = 0;
	while(q.size())
	{
		auto t = q.top();
		q.pop();
		int now = t.se;
		if(vis[now]) continue;
		vis[now] = 1;
		for(auto tt:e[now])
		{
			int spot = tt.fi,c = tt.se.fi,f = tt.se.se;
			if(dist[spot]>dist[now]+c&&f>=mid)
			{
				dist[spot] = dist[now]+c;
				q.push({dist[spot],spot});
			}
		}
	}
	return (dist[n]!=1e18);
}

signed main()
{
	IOS;
	cin>>n>>m;
	while(m--)
	{
		cin>>a>>b>>c>>f;
		e[a].pb({b,{c,f}});
		e[b].pb({a,{c,f}});
	}
	int maxn = -1e18;
	for(int i = 1;i<=1000;i++)
	{
		if(check(i))
		{
			int now = (double)i/dist[n]*1e6;
			maxn = max(maxn,now);
		} 
	}
	cout<<maxn;
	return 0;
}

[USACO3.2] 香甜的黄油 Sweet Butter

题目描述

Farmer John 发现了做出全威斯康辛州最甜的黄油的方法:糖。

把糖放在一片牧场上,他知道 N 只奶牛会过来舔它,这样就能做出能卖好价钱的超甜黄油。当然,他将付出额外的费用在奶牛上。

Farmer John 很狡猾。像以前的 Pavlov,他知道他可以训练这些奶牛,让它们在听到铃声时去一个特定的牧场。他打算将糖放在那里然后下午发出铃声,以至他可以在晚上挤奶。

Farmer John 知道每只奶牛都在各自喜欢的牧场(一个牧场不一定只有一头牛)。给出各头牛在的牧场和牧场间的路线,找出使所有牛到达的路程和最短的牧场(他将把糖放在那)。

输入格式

第一行包含三个整数 N,P,C,分别表示奶牛数、牧场数和牧场间道路数。

第二行到第 N+1 行,每行一个整数,其中第 i 行的整数表示第  i−1 头奶牛所在的牧场号。

第 N+2 行到第 N+C+1 行,每行包含三个整数 A,B,D,表示牧场号为 A 和 B 的两个牧场之间有一条长度为 D 的双向道路相连。

输出格式

输出一行一个整数,表示奶牛必须行走的最小的距离和。

#include<bits/stdc++.h>
#define ll long long
#define fi first
#define se second
#define pb push_back
#define PII pair<int,int > 
#define int long long
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)

using namespace std;

const int N = 1e6+5;

int w,n,m;
int dis[N];
bool vis[N];

map<int,int> mp;
vector<PII> v[N];

void dijkstra(int s)
{
	priority_queue<PII,vector<PII>,greater<PII> > q;
	for(int i=1;i<=n;i++)
	{
		dis[i] = 1e18;
		vis[i] = 0;
	}
	dis[s] = 0;
	q.push({0,s});
	while(q.size())
	{
		auto t = q.top();
		q.pop();
		int now = t.se;
		if(vis[now]==1) continue;
		vis[now] = 1;
		for(auto tt:v[now])
		{
			int spot = tt.fi,w = tt.se;
			if(dis[spot]>dis[now]+w)
			{
				dis[spot] = dis[now]+w;
				q.push({dis[spot],spot});
			}	
		}
	}
}
signed main()
{
	IOS;
	cin>>w>>n>>m;
	for(int i=1;i<=w;i++)
	{
		int a;
		cin>>a;
		mp[a]++;
	}
	while(m--)
	{
		int a,b,d;
		cin>>a>>b>>d;
		v[a].pb({b,d});
		v[b].pb({a,d});
	}
	int minn =1e18;
	for(int i=1;i<=n;i++)
	{
		dijkstra(i);
		int sum=0;
		for(int j=1;j<=n;j++)
		{
			sum += mp[j]*dis[j];
		}
		minn = min(minn,sum);
	} 
	cout<<minn;
	return 0; 
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值