【模板】单源最短路径(标准版)
题目背景
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;
}