kuangbin题单刷题详解(最短路篇)
中文题单链接(部分)
英文题单链接(完整)
4264. 规划最短路
题目描述
给定一个 n 个点 m 条边的有向图。
点的编号 1∼n。
图中可能包含重边和自环。
现在,我们要从点 A 前往点 B,请你同时规划出尽可能多的最短路线,要求任意两条路线均不含公共边。
输出可以同时规划出的最短路线的最大数量。
输入格式
第一行包含整数 T,表示共有 T 组测试数据。
每组数据第一行包含两个整数 n,m。
接下来 m 行,每行包含三个整数 a,b,c,表示存在一条从 a 到 b 的边,长度为 c。
最后一行包含两个整数 A 和 B,表示起始点和目标点。
输出格式
每行输出一个答案,一个整数表示可以同时规划出的最短路线的最大数量。
数据范围
1≤T≤65,
2≤n≤1000,
0≤m≤105,
1≤a,b≤n,
1≤c≤1000,
1≤A,B≤n,
A≠B。
输入样例:
3
7 8
1 2 1
1 3 1
2 4 1
3 4 1
4 5 1
4 6 1
5 7 1
6 7 1
1 7
6 7
1 2 1
2 3 1
1 3 3
3 4 1
3 5 1
4 6 1
5 6 1
1 6
2 2
1 2 1
1 2 2
1 2
输出样例:
2
1
1
解题思路
最短路上的边x-y满足dist(S,x)+w+dist(y,T)==dist(S,T), dist(S,x)建正向图跑最短路,dist(y,T)建反向图跑最短路,最后遍历所有边,将符合条件的边加入网络中,因为每条边只用一次,容量设为1即可。然后跑一遍最大流就能算出答案。
代码
// Problem: 规划最短路 // Contest: AcWing // URL: https://www.acwing.com/problem/content/4267/ // Memory Limit: 64 MB // Time Limit: 1000 ms // Time: 2022-08-09 15:38:43 // // Powered by CP Editor (https://cpeditor.org) //fw #include<iostream> #include<cstdio> #include<fstream> #include<algorithm> #include<cmath> #include<deque> #include<vector> #include<queue> #include<string> #include<cstring> #include<map> #include<stack> #include<set> #include<climits> #define int ll #define zp ios::sync_with_stdio(false);cin.tie(0); cout.tie(0); #define pii pair <int, int> #define endl '\n' #define pb push_back #define lc u<<1 #define rc u<<1|1 using namespace std; typedef long long ll; const int INF=0x3f3f3f3f; const int N=5e5+10; int h[N],ht[N],hs[N],e[N],ne[N],f[N],w[N],idx,q[N],cur[N],d[N],n,m,S,T,dist1[N],dist2[N]; bool st[N]; struct node { int a,b,c; }edge[N]; void add1(int a,int b,int c) { e[idx]=b;f[idx]=c;ne[idx]=h[a];h[a]=idx++; e[idx]=a;f[idx]=0;ne[idx]=h[b];h[b]=idx++; } void add(int h[],int a,int b,int c) { e[idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx++; } bool bfs() { memset(d,-1,sizeof d); d[S]=0;q[0]=S;cur[S]=h[S]; int hh=0,tt=0; while(hh<=tt) { int t=q[hh++]; for(int i=h[t];~i;i=ne[i]) { int ver=e[i]; if(d[ver]==-1&&f[i]) { d[ver]=d[t]+1; cur[ver]=h[ver]; if(ver==T)return true; q[++tt]=ver; } } } return false; } int find(int u,int limit) { if(u==T)return limit; int flow=0; for(int i=cur[u];~i&&flow<limit;i=ne[i]) { int ver=e[i];cur[u]=i; if(d[ver]==d[u]+1&&f[i]) { int t=find(ver,min(f[i],limit-flow)); if(!t)d[ver]=-1; f[i]-=t;f[i^1]+=t;flow+=t; } } return flow; } int dinic() { int r=0,flow; while(bfs())while(flow=find(S,INF))r+=flow; return r; } int dijkstra(int h[],int s,int dis[]) { memset(st,0,sizeof st); dis[s]=0; priority_queue<pii,vector<pii>,greater<pii>>heap; heap.push({0,s}); while(heap.size()) { auto t=heap.top(); heap.pop(); int ver=t.second; if(st[ver])continue; st[ver]=true; for(int i=h[ver];~i;i=ne[i]) { int j=e[i]; if(dis[j]>dis[ver]+w[i]) { dis[j]=dis[ver]+w[i]; heap.push({dis[j],j}); } } } } void remake() { idx=0; memset(h,-1,sizeof h); memset(hs,-1,sizeof hs); memset(ht,-1,sizeof ht); cin>>n>>m; for(int i=0;i<m;i++) { int a,b,c; cin>>a>>b>>c; edge[i]={a,b,c}; add(ht,a,b,c); add(hs,b,a,c); } cin>>S>>T; memset(dist1,0x3f,sizeof dist1); memset(dist2,0x3f,sizeof dist2); dijkstra(ht,S,dist1); dijkstra(hs,T,dist2); for(int i=0;i<m;i++) { int a=edge[i].a,b=edge[i].b,w=edge[i].c; if(dist1[a]+dist2[b]+w==dist1[T])add1(a,b,1); } cout<<dinic()<<endl; } signed main() { zp int T; cin>>T; while(T--)remake(); return 0; }
4240. 青蛙
题目描述
在二维平面上有 n 个石头。
第一个石头上有一个青蛙,第二个石头上也有一个青蛙。
第一个石头上的青蛙要到第二个石头上找另一个青蛙玩耍。
它可以直接跳到第二个石头上,也可以经过一系列中间石头,然后再跳到第二个石头上。
这只青蛙希望,它的跳跃过程中,单次跳跃的最长距离尽可能短。
请你计算并输出这个最长距离的最小可能值。
输入格式
输入包含多组测试数据。
每组数据第一行包含一个整数 n。
接下来 n 行,第 i 行包含两个整数 xi,yi,表示第 i 个石头的位置坐标。
每组数据输入完毕后,还会输入一个空行。
当输入 n=0 时,表示输入结束。
输出格式
每组数据先输出一行 Scenario #x,其中 x 是组别编号(从 1 开始)。
然后输出一行 Frog Distance = y,其中 y 是单次跳跃的最长距离的最小可能值,保留三位小数。
每组数据输出完毕后,还要输出一行空行(最后一组数据也不例外)。
数据范围
2≤n≤200,
0≤xi,yi≤1000。
输入样例:
2
0 0
3 4
3
17 4
19 4
18 5
0
输出样例:
Scenario #1
Frog Distance = 5.000
Scenario #2
Frog Distance = 1.414
最短路代码
//珍爱生命,远离double!!! //double不能memset成0x3f!!! //中间用int最后转double!!! // Problem: 青蛙 // Contest: AcWing // URL: https://www.acwing.com/problem/content/description/4243/ // Memory Limit: 64 MB // Time Limit: 3000 ms // Created Time: 2022-07-14 13:43:20 // // Powered by CP Editor (https://cpeditor.org) //fw #include<bits/stdc++.h> #define zp ios::sync_with_stdio(false);cin.tie(0); cout.tie(0); #define pii pair <int, int> #define endl '\n' #define pb push_back #define lc u<<1 #define rc u<<1|1 #define x first #define y second using namespace std; typedef long long ll; const int INF=0x3f3f3f3f; const int N=1e5+10; pii a[N]; int cnt,n; int h[N],e[N],ne[N],idx; int w[N],dist[N]; bool st[N]; void add(int a,int b,int c) { e[idx]=b; w[idx]=c; ne[idx]=h[a]; h[a]=idx++; } int get_len(int i,int j) { int dx=a[i].x-a[j].x; int dy=a[i].y-a[j].y; return (dx*dx+dy*dy); } void dijkstra() { priority_queue<pii,vector<pii>,greater<pii>>heap; heap.push({0,1}); while(heap.size()) { auto t=heap.top(); heap.pop(); int len=t.first; int ver=t.second; if(st[ver])continue; st[ver]=true; for(int i=h[ver];~i;i=ne[i]) { int j=e[i]; int mlen=max(dist[ver],w[i]); if(dist[j]>mlen) { dist[j]=mlen; heap.push({dist[j],j}); } } } } int main() { while(cin>>n&&n) { cnt++; idx=0; memset(h,-1,sizeof h); memset(st,0,sizeof st); memset(dist,0x3f,sizeof dist); dist[1]=0; for(int i=1;i<=n;i++)cin>>a[i].x>>a[i].y; for(int i(1);i<=n;i++) { for(int j(1);j<=n;j++){ if(i!=j){ int u=get_len(i,j); add(i,j,u); } } } dijkstra(); printf("Scenario #%d\n",cnt); printf("Frog Distance = %.3lf\n\n",sqrt(dist[2])); } return 0; }
最小生成树代码
// Problem: 青蛙 // Contest: AcWing // URL: https://www.acwing.com/problem/content/4243/ // Memory Limit: 64 MB // Time Limit: 3000 ms // Created Time: 2022-07-13 23:44:31 // // Powered by CP Editor (https://cpeditor.org) //fw #include<bits/stdc++.h> #define zp ios::sync_with_stdio(false);cin.tie(0); cout.tie(0); #define pii pair <int, int> #define pll pair <ll, ll> #define endl '\n' #define il inline #define pb push_back #define fi first #define se second #define lc u<<1 #define rc u<<1|1 using namespace std; typedef long long ll; const int INF=0x3f3f3f3f; const int N=1e5+10; struct edge{ int u,v; double len; }; vector<edge>v; struct node{ int x,y; }a[N]; int n,p[N]; int find(int x) { return x==p[x]?x:p[x]=find(p[x]); } double get_len(int i,int j) { double dx=a[i].x-a[j].x; double dy=a[i].y-a[j].y; return sqrt(dx*dx+dy*dy); } bool cmp(edge a,edge b) { return a.len<b.len; } int main() { int anscnt=0; while(cin>>n&&n) { anscnt++; for(int i=1;i<=n;i++) cin>>a[i].x>>a[i].y; for(int i=1;i<=n;i++) for(int j=1;j<i;j++) v.push_back({i,j,get_len(i,j)}),v.push_back({j,i,get_len(i,j)}); for(int i=1;i<=n;i++)p[i]=i; sort(v.begin(),v.end(),cmp); for(int i=0;i<v.size();i++) { int l=v[i].u,r=v[i].v; l=find(l),r=find(r); if(l!=r) { p[r]=l; } if(find(1)==find(2)) { printf("Scenario #%d\n",anscnt); printf("Frog Distance = %.3lf\n\n",v[i].len); break; } } } return 0; }
4241. 货物运输
题目描述
给定一个 n 个点 m 条边的无重边无自环的无向图。
点的编号为 1∼n。
现在,要从点 1 到点 n 运货。
已知每条边的最大承重,请你计算一次最多可以运多少货物。
输入格式
第一行包含整数 T,表示共有 T 组测试数据。
每组数据第一行包含两个整数 n,m。
接下来 m 行,每行三个整数 a,b,c,表示点 a 和点 b 之间有一条无向边,最大承重为 c。
输出格式
每组数据先输出一行 Scenario #x,其中 x 是组别编号(从 1 开始)。
然后一行,输出一个整数,表示一次性最多可运货物数量。
最后输出一个空行。
数据范围
1≤T≤10,
1≤n≤1000,
1≤m≤n(n−1)2,
1≤a,b≤n,
1≤c≤106。
保证一定有解。
输入样例:
1
3 3
1 2 3
1 3 4
2 3 5
输出样例:
Scenario #1:
4
最短路代码
// Problem: 货物运输 // Contest: AcWing // URL: https://www.acwing.com/problem/content/4244/ // Memory Limit: 64 MB // Time Limit: 1000 ms // Created Time: 2022-07-13 23:28:43 // // Powered by CP Editor (https://cpeditor.org) //fw #include<bits/stdc++.h> #define zp ios::sync_with_stdio(false);cin.tie(0); cout.tie(0); #define pii pair <int, int> #define pll pair <ll, ll> #define endl '\n' #define il inline #define pb push_back #define fi first #define se second #define lc u<<1 #define rc u<<1|1 using namespace std; typedef long long ll; const int INF=0x3f3f3f3f; const int N=1e3+10,M=2e6+5; int h[N],e[M],ne[M],w[M],idx; bool st[N]; int n,m; int dist[N]; void add(int a,int b,int c) { e[idx]=b; w[idx]=c; ne[idx]=h[a]; h[a]=idx++; } int dijkstra()//最小值最大 { priority_queue<pii>heap;//因为要更新最大值,所以用大根堆 memset(dist,-0x3f,sizeof dist);//因为后面要取max,所以未访问的点初始化成-INF dist[1]=INF;//1号是七点,没有前面管道的限制,可以初始化成INF heap.push({0,1}); while(heap.size()) { auto t=heap.top(); heap.pop(); int len=t.first,ver=t.second; if(st[ver])continue; st[ver]=true; for(int i=h[ver];~i;i=ne[i]) { int j=e[i],z=min(w[i],dist[ver]);//求较小值 if(dist[j]<z)//最大 { dist[j]=z; heap.push({dist[j],j}); } } } return dist[n]; } int main() { zp int T; int cnt=0; cin>>T; while(T--) { memset(h,-1,sizeof h); memset(st,0,sizeof st); idx=0; cin>>n>>m; while(m--) { int a,b,c; cin>>a>>b>>c; add(a,b,c); add(b,a,c); } cout<<"Scenario #"<<++cnt<<":"<<endl; cout<<dijkstra()<<endl; cout<<endl; } return 0; }
1132. 农场派对
题目描述
N 头牛要去参加在某农场举行的一场编号为 X 的牛的派对。
有 M 条有向道路,每条路长 Ti;每头牛参加完派对后都必须回到家,每头牛都会选择最短路径。
求这 N 头牛的最短路径(一个来回)中最长的一条的长度。
特别提醒:可能有权值不同的重边。
输入格式
第一行包含三个整数 N,M,X。
接下来 M 行,每行包含三个整数 Ai,Bi,Ti,表示有一条从 Ai 到 Bi 的路,长度为 Ti。
输出格式
共一行,一个数,表示最短路中最长的一条的长度。
数据范围
1≤N≤1000,
1≤X≤N,
1≤M≤105,
1≤Ti≤100
输入样例:
4 8 2
1 2 4
1 3 2
1 4 7
2 1 1
2 3 5
3 1 2
3 4 4
4 2 3
输出样例:
10
代码
// Problem: 农场派对 // Contest: AcWing // URL: https://www.acwing.com/problem/content/1134/ // Memory Limit: 64 MB // Time Limit: 1000 ms // Created Time: 2022-07-14 16:03:06 // // Powered by CP Editor (https://cpeditor.org) //fw #include<bits/stdc++.h> #define zp ios::sync_with_stdio(false);cin.tie(0); cout.tie(0); #define pii pair <int, int> #define endl '\n' #define pb push_back #define lc u<<1 #define rc u<<1|1 using namespace std; typedef long long ll; const int INF=0x3f3f3f3f; const int N=1e3+10,M=2e5+5; int h[N],e[M],ne[M],w[M],idx; int dist[N],n,m,x; int d1[N]; int A[M],B[M],C[M]; bool st[N]; void add(int a,int b,int c) { e[idx]=b; w[idx]=c; ne[idx]=h[a]; h[a]=idx++; } int dijkstra() { priority_queue<pii,vector<pii>,greater<pii>>heap; memset(dist,0x3f,sizeof dist); dist[x]=0; heap.push({0,x}); while(heap.size()) { auto t=heap.top(); heap.pop(); int dis=t.first,ver=t.second; if(st[ver])continue; st[ver]=true; for(int i=h[ver];~i;i=ne[i]) { int j=e[i]; if(dist[j]>dis+w[i]) { dist[j]=dis+w[i]; heap.push({dist[j],j}); } } } } int main() { memset(h,-1,sizeof h); cin>>n>>m>>x; for(int i=1;i<=m;i++)//将x视为起点,正向建图,计算从X走回各点的距离 { cin>>A[i]>>B[i]>>C[i]; add(A[i],B[i],C[i]); } dijkstra(); memcpy(d1,dist,sizeof dist);//保存结果 //建立反向图,计算从各点走向X的距离 idx=0; memset(h,-1,sizeof h); memset(st,0,sizeof st); memset(dist,0,sizeof dist); for(int i=1;i<=m;i++) { add(B[i],A[i],C[i]);//建反图 } dijkstra(); int ans=0; for(int i=1;i<=n;i++)ans=max(ans,d1[i]+dist[i]);//计算来回距离和 cout<<ans<<endl; return 0; }
904. 虫洞
题目描述
农夫约翰在巡视他的众多农场时,发现了很多令人惊叹的虫洞。
虫洞非常奇特,它可以看作是一条 单向 路径,通过它可以使你回到过去的某个时刻(相对于你进入虫洞之前)。
农夫约翰的每个农场中包含 N 片田地,M 条路径(双向)以及 W 个虫洞。
现在农夫约翰希望能够从农场中的某片田地出发,经过一些路径和虫洞回到过去,并在他的出发时刻之前赶到他的出发地。
他希望能够看到出发之前的自己。
请你判断一下约翰能否做到这一点。
下面我们将给你提供约翰拥有的农场数量 F,以及每个农场的完整信息。
已知走过任何一条路径所花费的时间都不超过 10000 秒,任何虫洞将他带回的时间都不会超过 10000 秒。
输入格式
第一行包含整数 F,表示约翰共有 F 个农场。
对于每个农场,第一行包含三个整数 N,M,W。
接下来 M 行,每行包含三个整数 S,E,T,表示田地 S 和 E 之间存在一条路径,经过这条路径所花的时间为 T。
再接下来 W 行,每行包含三个整数 S,E,T,表示存在一条从田地 S 走到田地 E 的虫洞,走过这条虫洞,可以回到 T 秒之前。
输出格式
输出共 F 行,每行输出一个结果。
如果约翰能够在出发时刻之前回到出发地,则输出 YES,否则输出 NO。
数据范围
1≤F≤5
1≤N≤500,
1≤M≤2500,
1≤W≤200,
1≤T≤10000,
1≤S,E≤N
输入样例:
2
3 3 1
1 2 2
1 3 4
2 3 1
3 1 3
3 2 1
1 2 3
2 3 4
3 1 8
输出样例:
NO
YES
代码
// Problem: 虫洞 // Contest: AcWing // URL: https://www.acwing.com/problem/content/description/906/ // Memory Limit: 64 MB // Time Limit: 1000 ms // Created Time: 2022-07-14 18:11:51 // // Powered by CP Editor (https://cpeditor.org) //fw #include<bits/stdc++.h> #define zp ios::sync_with_stdio(false);cin.tie(0); cout.tie(0); #define pii pair <int, int> #define endl '\n' #define pb push_back #define lc u<<1 #define rc u<<1|1 using namespace std; typedef long long ll; const int INF=0x3f3f3f3f; const int N=1e5+10; int h[N],e[N],ne[N],w[N],idx; int dist[N],n,m,p; int cnt[N]; bool st[N]; void add(int a,int b,int c) { e[idx]=b; w[idx]=c; ne[idx]=h[a]; h[a]=idx++; } bool spfa() { memset(dist,0,sizeof dist); memset(cnt,0,sizeof cnt); queue<int>q; for(int i=1;i<=n;i++)q.push(i),st[i]=true; while(q.size()) { int t=q.front(); q.pop(); st[t]=false; for(int i=h[t];~i;i=ne[i]) { int j=e[i]; if(dist[j]>dist[t]+w[i]) { dist[j]=dist[t]+w[i]; cnt[j]=cnt[t]+1; if(cnt[j]>=n)return true; if(!st[j]) { q.push(j); st[j]=true; } } } } return false; } void solve() { memset(h,-1,sizeof h); cin>>n>>m>>p; while(m--) { int a,b,c; cin>>a>>b>>c; add(a,b,c); add(b,a,c); } while(p--)//回到t秒前视为负权,判断是否存在负环即可 { int a,b,c; cin>>a>>b>>c; add(a,b,-c); } if(spfa())cout<<"YES"<<endl; else cout<<"NO"<<endl; } int main() { int T; cin>>T; while(T--)solve(); return 0; }
4243. 传递信息
题目描述
机房有 n 台服务器,编号 1∼n。
其中一些服务器之间可以互相传递信息。
不同服务器之间传递信息的时间可能不同。
请你计算,从 1 号服务器发出一个信息,传递到其他所有服务器,所需花费的最短时间。
输入格式
第一行包含整数 n。
第 2∼n 行,第 i 行包含 i−1 个正整数或字符 x,用来描述第 i 号服务器和前 i−1 号服务器的连接状况。如果第 j 个值 Aij 是正整数,则表示服务器 i 和服务器 j 之间可以相互传递信息,传递时间为 Aij;如果第 j 个值 Aij 是 x,则表示服务器 i 和服务器 j 之间无法相互传递信息。
输出格式
输出一个整数,表示所需花费的最短时间。
数据范围
1≤n≤100,
服务器之间的传递时间取值范围 [1,100]。
保证一定有解。
输入样例:
5
50
30 5
100 20 50
10 x x 10
输出样例:
35
代码
// Problem: 传递信息 // Contest: AcWing // URL: https://www.acwing.com/problem/content/description/4246/ // Memory Limit: 64 MB // Time Limit: 1000 ms // Created Time: 2022-07-14 20:53:40 // // Powered by CP Editor (https://cpeditor.org) //fw #include<bits/stdc++.h> #include<sstream> #define zp ios::sync_with_stdio(false);cin.tie(0); cout.tie(0); #define pii pair <int, int> #define endl '\n' #define pb push_back #define lc u<<1 #define rc u<<1|1 using namespace std; typedef long long ll; const int INF=0x3f3f3f3f; const int N=1e2+10; int f[N][N],n; int main() { int n; cin>>n; memset(f,0x3f,sizeof f); for(int i=1;i<=n;i++) for(int j=1;j<i;j++) { string s; cin>>s; if(s!="x") { stringstream ss(s); int a; ss>>a; f[i][j]=f[j][i]=a; } } //水题,数据范围只有100,直接跑个floyd后求个最大值就行 for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { f[i][j]=min(f[i][j],f[i][k]+f[k][j]); } int ans=-0x3f3f3f3f; for(int i=2;i<=n;i++) ans=max(ans,f[1][i]); cout<<ans<<endl; return 0; }
4244. 牛的比赛
题目描述
N 头奶牛,编号 1∼N,一起参加比赛。
奶牛的战斗力两两不同。
这些奶牛之间已经进行了 M 轮两两对决。
在对决中,战斗力高的奶牛一定会战胜战斗力低的奶牛。
请问,通过上述 M 轮对决的结果,可以确定多少头奶牛的具体战斗力排名。
输入格式
第一行包含两个整数 N,M。
接下来 M 行,每行包含两个整数 a,b,表示奶牛 a 和奶牛 b 之间进行了对决,并且奶牛 a 战胜了奶牛 b。
输出格式
输出可以确定具体战斗力排名的奶牛数量。
数据范围
1≤N≤100,
1≤M≤4500,
数据保证合法。
输入样例:
5 5
4 3
4 2
3 2
1 2
2 5
输出样例:
2
样例解释
2 号奶牛输给了 1,3,4 号奶牛,战胜了 5 号奶牛,可以确定它的战斗力排名为 4。
5 号奶牛输给了排在第 4 的 2 号奶牛,所以它的战斗力排名为 5。
其它奶牛不确定。
代码
// Problem: 牛的比赛 // Contest: AcWing // URL: https://www.acwing.com/problem/content/4247/ // Memory Limit: 64 MB // Time Limit: 1000 ms // Created Time: 2022-07-14 21:06:58 // // Powered by CP Editor (https://cpeditor.org) //fw #include<bits/stdc++.h> #define zp ios::sync_with_stdio(false);cin.tie(0); cout.tie(0); #define pii pair <int, int> #define endl '\n' #define pb push_back #define lc u<<1 #define rc u<<1|1 using namespace std; typedef long long ll; const int INF=0x3f3f3f3f; const int N=1e2+10; int n,m,f[N][N]; int main() { //传递闭包板子题.. cin>>n>>m; for(int i=1;i<=n;i++)f[i][i]=1;//自己与自己的关系确定 while(m--) { int a,b; cin>>a>>b; f[a][b]=1; } //传递闭包 for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) f[i][j]|=(f[i][k]&f[k][j]); int ans=0; for(int i=1;i<=n;i++) { int cnt=0; for(int j=1;j<=n;j++) cnt+=f[i][j]|f[j][i]; if(cnt==n)ans++;//如果能确定和所有牛的关系答案就加一 } cout<<ans<<endl; return 0; }
4246. 最短路径和
题目描述
给定一个 n 个点 m 条边的无重边无自环的有向图。
点的编号 1∼n。
任意两点之间均可相互抵达。
请你计算,从点 1 到其他所有点的最短路径长度以及从其他所有点到点 1 的最短路径长度,并将这 2×(n−1) 个最短路径长度相加后输出结果。
输入格式
第一行包含整数 T,表示共有 T 组测试数据。
每组数据第一行包含两个整数 n,m。
接下来 m 行,每行包含三个正整数 a,b,c,表示从点 a 到点 b 存在一条边,长度为 c。
输出格式
每组数据输出一行,一个整数表示结果。
数据范围
1≤T≤10,
1≤n,m≤106,
1≤a,b≤n,
一组数据中所有的 c 相加之和不超过 109。
输入样例:
2
2 2
1 2 13
2 1 33
4 6
1 2 10
2 1 60
1 3 20
3 4 10
2 4 5
4 1 50
输出样例:
46
210
代码
// Problem: 最短路径和 // Contest: AcWing // URL: https://www.acwing.com/problem/content/description/4249/ // Memory Limit: 64 MB // Time Limit: 3000 ms // Created Time: 2022-07-14 21:17:57 // // Powered by CP Editor (https://cpeditor.org) //fw #include<bits/stdc++.h> #define zp ios::sync_with_stdio(false);cin.tie(0); cout.tie(0); #define pii pair <int, int> #define endl '\n' #define pb push_back #define lc u<<1 #define rc u<<1|1 using namespace std; typedef long long ll; const int INF=0x3f3f3f3f; const int N=1e6+10; int h[N],e[N],ne[N],w[N],idx; int A[N],B[N],C[N]; int dist[N],n,m; bool st[N]; void add(int a,int b,int c) { e[idx]=b; w[idx]=c; ne[idx]=h[a]; h[a]=idx++; } void dijkstra() { memset(dist,0x3f,sizeof dist); dist[1]=0; priority_queue<pii,vector<pii>,greater<pii>>heap; heap.push({0,1}); while(heap.size()) { auto t=heap.top(); heap.pop(); int ver=t.second,dis=t.first; if(st[ver])continue; st[ver]=true; for(int i=h[ver];~i;i=ne[i]) { int j=e[i]; if(dist[j]>dis+w[i]) { dist[j]=dis+w[i]; heap.push({dist[j],j}); } } } } int main() { //大水题,建正图反图分别跑一遍dijkstra即可 int T; cin>>T; while(T--) { idx=0; ll ans=0; memset(h,-1,sizeof h); memset(st,0,sizeof st); cin>>n>>m; for(int i=1;i<=m;i++) { cin>>A[i]>>B[i]>>C[i]; add(A[i],B[i],C[i]); } dijkstra(); for(int i=1;i<=n;i++)ans+=dist[i]; idx=0; memset(h,-1,sizeof h); memset(st,0,sizeof st); for(int i=1;i<=m;i++) { add(B[i],A[i],C[i]); } dijkstra(); for(int i=1;i<=n;i++)ans+=dist[i]; cout<<ans<<endl; } return 0; }
4247. 糖果
题目描述
有 n 个小朋友,编号 1∼n。
老师要给他们发糖果。
小朋友们的攀比心都很重,现在给出 m 条攀比信息。
每条信息包含三个整数 a,b,c,含义是小朋友 a 认为小朋友 b 的糖果数量最多只可以比他多 c 个,否则他就生气。
老师在发糖果时,必须照顾所有小朋友的情绪,让他们都感到满意。
请问,小朋友 n 最多比小朋友 1 多分到多少个糖果。
输入格式
第一行包含两个整数 n,m。
接下来 m 行,每行包含三个整数 a,b,c,表示一条攀比信息。
输出格式
一个整数,表示小朋友 n 最多比小朋友 1 多分到的糖果数量的最大可能值。
代码
// Problem: 糖果 // Contest: AcWing // URL: https://www.acwing.com/problem/content/description/4250/ // Memory Limit: 64 MB // Time Limit: 3000 ms // Created Time: 2022-07-14 21:55:10 // // Powered by CP Editor (https://cpeditor.org) //fw #include<bits/stdc++.h> #define zp ios::sync_with_stdio(false);cin.tie(0); cout.tie(0); #define pii pair <int, int> #define endl '\n' #define pb push_back #define lc u<<1 #define rc u<<1|1 using namespace std; typedef long long ll; const int INF=0x3f3f3f3f; const int N=3e4+5,M=2e5+10; int h[N],e[M],ne[M],w[M],idx; int dist[N],n,m; bool st[N]; void add(int a,int b,int c) { e[idx]=b; w[idx]=c; ne[idx]=h[a]; h[a]=idx++; } void dijkstra() { priority_queue<pii,vector<pii>,greater<pii>>heap; heap.push({0,1}); dist[1]=0; while(heap.size()) { auto t=heap.top(); heap.pop(); int ver=t.second,dis=t.first; if(st[ver])continue; st[ver]=true; for(int i=h[ver];~i;i=ne[i]) { int j=e[i]; if(dist[j]>dis+w[i]) { dist[j]=dis+w[i]; heap.push({dist[j],j}); } } } } int main() { //差分约束 //用三角不等式//dist[b]<=dist[a]+c代表a,b之间糖果的数量关系 //小朋友 n 最多比小朋友 1 多分到多少个糖果 // “最多”, 因为会有多个小于号要同时满足,所有应满足所有小于条件里最小的那个,故跑最短路 //反之,“最少”应该跑最长路 cin>>n>>m; memset(h,-1,sizeof h); memset(dist,0x3f,sizeof dist); for(int i=1;i<=m;i++) { int a,b,c; cin>>a>>b>>c; add(a,b,c); } dijkstra(); cout<<dist[n]<<endl; return 0; }
903. 昂贵的聘礼
题目描述
年轻的探险家来到了一个印第安部落里。
在那里他和酋长的女儿相爱了,于是便向酋长去求亲。
酋长要他用 10000 个金币作为聘礼才答应把女儿嫁给他。
探险家拿不出这么多金币,便请求酋长降低要求。
酋长说:”嗯,如果你能够替我弄到大祭司的皮袄,我可以只要 8000 金币。如果你能够弄来他的水晶球,那么只要 5000 金币就行了。”
探险家就跑到大祭司那里,向他要求皮袄或水晶球,大祭司要他用金币来换,或者替他弄来其他的东西,他可以降低价格。
探险家于是又跑到其他地方,其他人也提出了类似的要求,或者直接用金币换,或者找到其他东西就可以降低价格。
不过探险家没必要用多样东西去换一样东西,因为不会得到更低的价格。
探险家现在很需要你的帮忙,让他用最少的金币娶到自己的心上人。
另外他要告诉你的是,在这个部落里,等级观念十分森严。
地位差距超过一定限制的两个人之间不会进行任何形式的直接接触,包括交易。
他是一个外来人,所以可以不受这些限制。
但是如果他和某个地位较低的人进行了交易,地位较高的的人不会再和他交易,他们认为这样等于是间接接触,反过来也一样。
因此你需要在考虑所有的情况以后给他提供一个最好的方案。
为了方便起见,我们把所有的物品从 1 开始进行编号,酋长的允诺也看作一个物品,并且编号总是 1。
每个物品都有对应的价格 P,主人的地位等级 L,以及一系列的替代品 Ti 和该替代品所对应的”优惠” Vi。
如果两人地位等级差距超过了 M,就不能”间接交易”。
你必须根据这些数据来计算出探险家最少需要多少金币才能娶到酋长的女儿。
输入格式
输入第一行是两个整数 M,N,依次表示地位等级差距限制和物品的总数。
接下来按照编号从小到大依次给出了 N 个物品的描述。
每个物品的描述开头是三个非负整数 P、L、X,依次表示该物品的价格、主人的地位等级和替代品总数。
接下来 X 行每行包括两个整数 T 和 V,分别表示替代品的编号和”优惠价格”。
输出格式
输出最少需要的金币数。
数据范围
1≤N≤100,
1≤P≤10000,
1≤L,M≤N,
0≤X<N
输入格式
1 4
10000 3 2
2 8000
3 5000
1000 2 1
4 200
3000 2 1
4 200
50 2 0
输出格式
5250
代码
// Problem: 昂贵的聘礼 // Contest: AcWing // URL: https://www.acwing.com/problem/content/905/ // Memory Limit: 64 MB // Time Limit: 1000 ms // Time: 2022-07-24 09:44:11 // // Powered by CP Editor (https://cpeditor.org) // fw #include <algorithm> #include <cstring> #include <iostream> #define zp \ ios::sync_with_stdio(false); \ cin.tie(0); \ cout.tie(0); #define pii pair<int, int> #define endl '\n' #define pb push_back #define lc u << 1 #define rc u << 1 | 1 using namespace std; typedef long long ll; const int INF = 0x3f3f3f3f; const int N = 110 + 10; int n, m, w[N][N], level[N], dist[N]; bool st[N]; int dijkstra(int down, int up) { memset(dist, 0x3f, sizeof dist); memset(st, 0, sizeof st); dist[0] = 0; for (int i = 1; i <= n + 1; i++) { int t = -1; for (int j = 0; j <= n; j++) if (!st[j] && (t == -1 || dist[t] > dist[j])) t = j; st[t] = true; for (int j = 1; j <= n; j++) if (level[j] >= down && level[j] <= up) dist[j] = min(dist[j], dist[t] + w[t][j]); } return dist[1]; } int main() { cin >> m >> n; memset(w, 0x3f, sizeof w); for (int i = 1; i <= n; i++) w[i][i] = 0; for (int i = 1; i <= n; i++) { int price, cnt; cin >> price >> level[i] >> cnt; w[0][i] = min(price, w[0][i]);//建立超级源点,相当于用所有金币直接买这个物品 while (cnt--) { int id, cost; cin >> id >> cost; w[id][i] = min(w[id][i], cost); } } int res = INF; for (int i = level[1] - m; i <= level[1]; i++)//枚举等级区间,要把1号点包含在内才行 res = min(res, dijkstra(i, i + m)); cout << res << endl;//输出结果 return 0; }
4249. 电车
题目描述
某城市共有 n 个电车站点,编号 1∼n。
现在,我们要乘坐电车从 A 站点前往 B 站点。
电车在第 i 个站点可以驶往 ki 个其他站点,其中一个站点为默认驶往站点,如果电车驶往非默认站点,则需要进行一次变道。
我们希望从 A 到 B 的过程中,电车变道的次数尽可能少。
请问,最少需要进行多少次变道。
输入格式
第一行包含三个整数 n,A,B。
接下来 n 行,其中第 i 行首先包含一个整数 ki,表示第 i 个站点可以驶往的站点数量,然后包含 ki 个整数,表示每个可驶往的站点编号,其中第 1 个给出的站点为默认驶往站点。
输出格式
一个整数,表示最少变道次数。
如果无法从 A 到 B,则输出 −1。
数据范围
2≤N≤100 ,
1≤A,B≤N,
0≤Ki≤N−1。
输入样例:
3 2 1
2 2 3
2 3 1
2 1 2
输出样例:
0
代码
//大水题 // Problem: 电车 // Contest: AcWing // URL: https://www.acwing.com/problem/content/description/4252/ // Memory Limit: 64 MB // Time Limit: 1000 ms // Time: 2022-07-24 22:40:23 // // Powered by CP Editor (https://cpeditor.org) //fw #include<iostream> #include<cstdio> #include<fstream> #include<algorithm> #include<cmath> #include<deque> #include<vector> #include<queue> #include<string> #include<cstring> #include<map> #include<stack> #include<set> #include<climits> #define zp ios::sync_with_stdio(false);cin.tie(0); cout.tie(0); #define pii pair <int, int> #define endl '\n' #define pb push_back #define lc u<<1 #define rc u<<1|1 using namespace std; typedef long long ll; const int INF=0x3f3f3f3f; const int N=1e5+10; int h[N],e[N],ne[N],w[N],idx,dist[N]; int n,s,t; bool st[N]; void add(int a,int b,int c) { e[idx]=b; w[idx]=c; ne[idx]=h[a]; h[a]=idx++; } int dijkstra() { memset(dist,0x3f,sizeof dist); dist[s]=0; priority_queue<pii,vector<pii>,greater<pii>>heap; heap.push({0,s}); while(heap.size()) { auto t=heap.top(); heap.pop(); int ver=t.second,dis=t.first; if(st[ver])continue; st[ver]=true; for(int i=h[ver];~i;i=ne[i]) { int j=e[i]; if(dist[j]>dis+w[i]) { dist[j]=dis+w[i]; heap.push({dist[j],j}); } } } if(dist[t]==INF) return -1; else return dist[t]; } int main() { memset(h,-1,sizeof h); cin>>n>>s>>t; int x; int k; for(int i=1;i<=n;i++) { cin>>x; if(x!=0) { cin>>k; add(i,k,0); } for(int j=1;j<=x-1;j++) { cin>>k; add(i,k,1); } } cout<<dijkstra()<<endl; return 0; }
4251. Nya图最短路
题目描述
Nya 图是一种分层的无向图。
图中共有 N 个节点,每个节点都处于某一层当中。
层与层之间存在阶梯。
利用阶梯可以从任意一层的任意一个节点移动至其相邻层(上一层或下一层)的任意一个节点。
每次利用阶梯进行移动都需要花费固定的成本 C。
此外,还有 M 个点对之间存在额外边。
如果点 u 和点 v 之间存在额外边,则可以通过额外边在点 u 和点 v 之间进行双向移动。
每次利用额外边进行移动都需要花费一定的成本,不同额外边的花费可能不同。
请你计算从点 1 移动至点 N 所需花费的最低成本。
输入格式
第一行包含一个整数 T,表示共有 T 组测试数据。
每组数据第一行包含三个整数 N,M,C。
第二行包含 N 个整数 li,表示每个点所在的具体层数。
接下来 M 行,每行包含三个整数 u,v,w,表示点 u 和点 v 之间存在一条额外边,成本为 w。
输出格式
每组数据输出一行答案,具体格式为 Case #x: y,其中 x 为组别编号(从 1 开始),y 为所需花费的最低成本。如果无法到达点 N,则为 −1。
数据范围
1≤T≤20,
0≤N,M≤105,
1≤C≤103 ,
1≤li≤N,
1≤u,v≤N,
u≠v,
1≤w≤104。
输入样例:
2
3 3 3
1 3 2
1 2 1
2 3 1
1 3 3
3 3 3
1 3 2
1 2 2
2 3 2
1 3 4
输出样例:
Case #1: 2
Case #2: 3
代码
// Problem: Nya图最短路 // Contest: AcWing // URL: https://www.acwing.com/problem/content/4254/ // Memory Limit: 64 MB // Time Limit: 5000 ms // Time: 2022-07-24 23:05:56 // // Powered by CP Editor (https://cpeditor.org) //fw #include<iostream> #include<cstdio> #include<fstream> #include<algorithm> #include<cmath> #include<deque> #include<vector> #include<queue> #include<string> #include<cstring> #include<map> #include<stack> #include<set> #include<climits> #define zp ios::sync_with_stdio(false);cin.tie(0); cout.tie(0); #define pii pair <int, int> #define endl '\n' #define pb push_back #define lc u<<1 #define rc u<<1|1 using namespace std; typedef long long ll; const int INF=0x3f3f3f3f; const int N=2e5+10,M=3*N; int n,m,c,flo; ll h[N],e[M],ne[M],w[M],idx; int dist[N]; int st[N],f[N]; vector<int>v[N]; void add(ll a,ll b,ll c) { e[idx]=b; w[idx]=c; ne[idx]=h[a]; h[a]=idx++; } int dijkstra() { memset(dist,0x3f,sizeof dist); dist[1]=0; priority_queue<pii,vector<pii>,greater<pii>>heap; heap.push({0,1}); while(heap.size()) { auto t=heap.top();heap.pop(); int dis=t.first,ver=t.second; if(st[ver])continue; st[ver]=true; for(int i=h[ver];~i;i=ne[i]) { int j=e[i]; if(dist[j]>dist[ver]+w[i]) { dist[j]=dist[ver]+w[i]; heap.push({dist[j],j}); } } } if(dist[n]==INF)return -1; else return dist[n]; } int main() { int TT; cin>>TT; for(int T=1;T<=TT;T++) { memset(h,-1,sizeof h); memset(st,0,sizeof st); idx=0; cin>>n>>m>>c; for(int i=1;i<=n;i++) { cin>>f[i]; } for(int i=1;i<=n;i++) { add(i,f[i]+n+1,0);//将每一层的所有节点中连向一个虚拟点 //向相邻层连边 add(f[i]+n+2,i,c); add(f[i]+n,i,c); } while(m--) { int a,b,c; cin>>a>>b>>c; add(a,b,c); add(b,a,c); } printf("Case #%d: %d\n",T,dijkstra()); } return 0; }
1170. 排队布局
题目描述
当排队等候喂食时,奶牛喜欢和它们的朋友站得靠近些。
农夫约翰有 N 头奶牛,编号从 1 到 N,沿一条直线站着等候喂食。
奶牛排在队伍中的顺序和它们的编号是相同的。
因为奶牛相当苗条,所以可能有两头或者更多奶牛站在同一位置上。
如果我们想象奶牛是站在一条数轴上的话,允许有两头或更多奶牛拥有相同的横坐标。
一些奶牛相互间存有好感,它们希望两者之间的距离不超过一个给定的数 L。
另一方面,一些奶牛相互间非常反感,它们希望两者间的距离不小于一个给定的数 D。
给出 ML 条关于两头奶牛间有好感的描述,再给出 MD 条关于两头奶牛间存有反感的描述。
你的工作是:如果不存在满足要求的方案,输出-1;如果 1 号奶牛和 N 号奶牛间的距离可以任意大,输出-2;否则,计算出在满足所有要求的情况下,1 号奶牛和 N 号奶牛间可能的最大距离。
输入格式
第一行包含三个整数 N,ML,MD。
接下来 ML 行,每行包含三个正整数 A,B,L,表示奶牛 A 和奶牛 B 至多相隔 L 的距离。
再接下来 MD 行,每行包含三个正整数 A,B,D,表示奶牛 A 和奶牛 B 至少相隔 D 的距离。
输出格式
输出一个整数,如果不存在满足要求的方案,输出-1;如果 1 号奶牛和 N 号奶牛间的距离可以任意大,输出-2;否则,输出在满足所有要求的情况下,1 号奶牛和 N 号奶牛间可能的最大距离。
数据范围
2≤N≤1000,
1≤ML,MD≤104,
1≤L,D≤106
输入样例:
4 2 1
1 3 10
2 4 20
2 3 3
输出样例:
27
代码
#include<iostream> #include<cstring> using namespace std; const int N=1e3+10,M=2e6+10; const int INF=0x3f3f3f3f; int h[N],e[M],ne[M],w[M],idx; int dist[N],n,m1,m2,q[N],cnt[N]; bool st[N]; void add(int a,int b,int c) { e[idx]=b; w[idx]=c; ne[idx]=h[a]; h[a]=idx++; } bool spfa(int size) { int hh=0,tt=0; memset(dist,0x3f,sizeof dist); memset(st,0,sizeof st); memset(cnt,0,sizeof cnt); for(int i=1;i<=size;i++) { dist[i]=0; q[tt++]=i; st[i]=true; } while(hh!=tt) { int t=q[hh++]; if(hh==N)hh=0; st[t]=false; for(int i=h[t];~i;i=ne[i]) { int j=e[i]; if(dist[j]>dist[t]+w[i]) { dist[j]=dist[t]+w[i]; cnt[j]=cnt[t]+1; if(cnt[j]>=n)return true; if(!st[j]) { q[tt++]=j; if(tt==N)tt=0; st[j]=true; } } } } return false; } int main() { memset(h, -1, sizeof h); cin>>n>>m1>>m2; for(int i=1;i<=n;i++)add(i+1,i,0); while(m1--) { int a,b,c; cin>>a>>b>>c; if(b<a)swap(a,b); add(a,b,c); } while(m2--) { int a,b,c; cin>>a>>b>>c; if(b<a)swap(a,b); add(b,a,-c); } if(spfa(n))cout<<-1<<endl; else { spfa(1); if(dist[n]==INF)cout<<-2<<endl; else cout<<dist[n]<<endl; } }
4265. 0或1
题目描述
给定一个 n×n 的矩阵 Cij(1≤i,j≤n)。
我们想找到一个 n×n 的 01 矩阵 Xij(1≤i,j≤n)。
Xij 应满足以下条件:
X12+X13+…+X1n=1。
X1n+X2n+…Xn−1n=1。
对于每个 i(1<i<n),满足 ∑Xki(1≤k≤n)=∑Xij(1≤j≤n)。
例如,当 n=4 时,需满足:
X12+X13+X14=1
X14+X24+X34=1
X12+X22+X32+X42=X21+X22+X23+X24
X13+X23+X33+X43=X31+X32+X33+X34
现在,我们想知道你能得到的 ∑Cij×Xij(1≤i,j≤n) 的最小值。
输入格式
输入包含多组测试数据。
每组数据第一行包含整数 n。
接下来 n 行,每行 n 个整数,用来描述矩阵 Cij。
输出格式
每个测试数据输出一行结果,表示你能得到的 ∑Cij×Xij(1≤i,j≤n) 的最小值。
数据范围
输入最多包含 35 组数据。
2≤n≤300,
0≤Cij≤105。
输入样例:
4
1 2 4 10
2 0 1 1
2 2 0 5
6 3 1 2
输出样例:
3
代码
// Problem: 0或1 // Contest: AcWing // URL: https://www.acwing.com/problem/content/4268/ // Memory Limit: 64 MB // Time Limit: 1000 ms // Time: 2022-07-26 08:37:52 // // Powered by CP Editor (https://cpeditor.org) //fw #include<iostream> #include<cstdio> #include<fstream> #include<algorithm> #include<cmath> #include<deque> #include<vector> #include<queue> #include<string> #include<cstring> #include<map> #include<stack> #include<set> #include<climits> #define zp ios::sync_with_stdio(false);cin.tie(0); cout.tie(0); #define pii pair <int, int> #define endl '\n' #define pb push_back #define lc u<<1 #define rc u<<1|1 using namespace std; typedef long long ll; const int INF=0x3f3f3f3f; const int N=3e2+10; int c[N][N],d[N]; bool st[N];int n; void spfa(int u) { queue<int>q; for(int i=1;i<=n;i++) { if(i==u) { d[i]=INF; st[i]=0; } else { d[i]=c[u][i]; st[i]=1; q.push(i); } } while(q.size()) { auto t=q.front(); q.pop(); st[t]=0; for(int i=1;i<=n;i++) { if(d[i]>d[t]+c[t][i]) { d[i]=d[t]+c[t][i]; if(st[i]==0) { st[i]=1; q.push(i); } } } } } int main() { //抽象成图论模型 //C矩阵相当于邻接矩阵、 //条件一相当于1号点的出度为0 //条件二相当于n号点的入度为0 //条件三相当于中间点的度相等 //所以求1-n的最短路和从1和n出发点最小环就行 while(cin>>n) { for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { cin>>c[i][j]; } spfa(1); int dis1=d[1]; int dis2=d[n]; spfa(n); int dis3=d[n]; cout<<min(dis2,dis1+dis3)<<endl; } return 0; }
本文作者:Avarice_Zhao
本文链接:https://www.cnblogs.com/avarice/p/16476105.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步