kuangbin帶你飛:點擊進入新世界
最短路算法模板:點擊進入新世界
總結:
本人算是初學者中的初學者,歡迎交流~
kuangbin的專題確實是理解最短路的一大途徑,這篇博客主要記錄題解,順便總結最短路的題型。
A.模板題 參考1 . 6 . 10
B.變形最短路 (求最大邊權的最小值or最小邊權的最大值) 參考2 . 3
C.變形最短路 (求往返最短路的最大值) 參考4
D.變形最短路 (求往返最短路之和) 參考5
E.含負權邊的最短路 (spfa判負環) 參考 7 . 11 . 12
F. 確定排名 參考8
G.差分約束系統 (<=最短路 and >=最長路) 參考9 . 11
ps:跑最短路求得就是最大的距離,跑最長路求的就是最小的距離
H.判正環 參考 13 . 14
差分約束系統介紹
最短路其實不難,難點在於建圖,剩下五道題沒靈感,等國慶後補。
kuangbin之外:
最短路計數
最短路+二分
最短路+dp
分層圖最短路
文章目錄
1.Til the Cows Come Hom
原題鏈接:傳送門
思路:
- 經典最短路模板題,注意一下輸入的坑,起點爲n,終點爲1,其他沒有什麼問題。
- VJ不能用萬能頭!!!VJ不能用萬能頭!!!VJ不能用萬能頭!!!
代碼如下:
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<string>
#define R register int
using namespace std;
const int manx=1e4+5;
const int mamx=1e4+5;
int head[manx],d[manx];
bool vis[manx];
int n,m,k=0,s,e;
struct node{
int v,next,w;
}a[manx];
void add(int u,int v,int w)
{
a[++k].next=head[u];
a[k].w=w;
a[k].v=v;
head[u]=k;
}
void dij()
{
memset(d,0x3f,sizeof(d));
memset(vis,0,sizeof(vis));
d[s]=0;
priority_queue<pair<int,int> >q;
q.push(make_pair(0,s));
while(q.size()){
int u=q.top().second;
q.pop();
if(vis[u]) continue;
vis[u]=1;
for(int i=head[u];i;i=a[i].next){
int v=a[i].v,w=a[i].w;
if(d[v]>d[u]+w) d[v]=d[u]+w,q.push(make_pair(-d[v],v));
}
}
}
int main()
{
scanf("%d%d",&m,&n);
e=1,s=n;
for(int i=1;i<=m;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
dij();
cout<<d[e];
return 0;
}
2.Frogger
原題鏈接:傳送門
思路:
- 變形最短路求最大邊權的最小值。
- 由於題目給的數據過小,可以考慮floyd,不過 d[i][j] 不在表示點i到點j 的最短路,而是點i到點j路徑中最大邊權的最小值,可以通過改變鬆弛操作實現:
- 原先的鬆弛操作:d[i][j]= min( d[i][j], d[i][k] + d[k][j]) ;
- 本題的鬆弛操作:d[i][j]= min( d[i][j] ,max( d[i][k] , d[k][j] );
AC代碼如下:
// #include<bits/stdc++.h>
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#define R register int
#define mm make_pair
using namespace std;
const int manx=300;
int x[manx],y[manx];
double d[manx][manx];
int main()
{
int n,m,f=1;
while(scanf("%d",&n)!=EOF&&n)
{
memset(d,0,sizeof(d));
for(int i=1;i<=n;i++)
scanf("%d%d",&x[i],&y[i]);
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
d[i][j]=d[j][i]=sqrt(pow((x[i]-x[j])*1.0,2)+pow((y[i]-y[j])*1.0,2));
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
d[i][j]=min(d[i][j],max(d[i][k],d[k][j]));
cout<<"Scenario #"<<f++<<endl;
printf("Frog Distance = %.3lf\n\n",d[1][2]);
}
return 0;
}
3.Heavy Transportation
原題鏈接:傳送門
思路:
- 同上,本題爲變形最短路,但這裏求的是最小邊的最大值並且n的範圍到達了1e3,O(n^3)的時間複雜度已經不夠看了,這裏我用的是spfa。
- 同樣鬆弛操作需要修改一番。
- 原本的鬆弛操作 : d[v]= min(d[v], d[u]+w);
- 本題的鬆弛操作 : d[v]= max(d[v] , min(d[u], w);
- 這裏d[v]表示的是到達v點的所有路徑中最小邊的最大值。
AC代碼如下:
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<string>
using namespace std;
const int manx=1e6+5;
const int mamx=1e6+5;
int head[manx],d[manx];
bool vis[manx];
struct node{
int v,w,next;
}a[mamx];
int n,m,s,e,k=0;
void add(int u,int v,int w)
{
a[++k].next=head[u];
a[k].w=w;
a[k].v=v;
head[u]=k;
}
void spfa()
{
memset(d,0,sizeof(d));
memset(vis,0,sizeof(vis));
d[s]=1e9;
queue<int>q;
q.push(s);
while(q.size())
{
int u=q.front();
q.pop();
vis[u]=0;
for(int i=head[u];i;i=a[i].next)
{
int v=a[i].v,w=a[i].w;
if(d[v]<min(d[u],w)) {
d[v]=min(d[u],w);
if(!vis[v])
q.push(v),vis[v]=1;
}
}
}
}
int main()
{
int t;
cin>>t;
for(int o=1;o<=t;o++)
{
scanf("%d%d",&n,&m);
s=1,e=n,k=0;
memset(head,-1,sizeof(head));
for(int i=1;i<=m;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
spfa();
printf("Scenario #%d:\n",o);
printf("%d\n",d[e]);
printf("\n");
}
return 0;
}
4.Silver Cow Party
原題鏈接:傳送門
思路:
- 求所有點到源點的最短往返路程的最大值。
- 同理,n的範圍達到1e3,不能用floyd,不然直接 min( d[i][v]+ d[v][i]) 完事。
- 這裏我是用dij,對於每個點都跑一次dij,開二維數組保存每個點的最短路。
AC代碼如下:
// #include<bits/stdc++.h>
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#define R register int
#define mm make_pair
using namespace std;
const int manx=1e3+5;
const int mamx=1e6+5;
struct node{
int v,next,w;
}a[mamx];
int d[manx][manx],head[manx];
bool vis[manx];
int n,m,s,e,k=0;
void add(int u,int v,int w)
{
a[++k].next=head[u];
head[u]=k;
a[k].v=v;
a[k].w=w;
}
void dij()
{
for(int i=1;i<=n;i++) d[s][i]=1e9;
memset(vis,0,sizeof(vis));
priority_queue<pair<int,int> >q;
d[s][s]=0;
q.push(make_pair(0,s));
while(q.size()){
int u=q.top().second;
q.pop();
if(vis[u]) continue;
vis[u]=1;
for(int i=head[u];i;i=a[i].next)
{
int v=a[i].v,w=a[i].w;
if(d[s][v]>d[s][u]+w)
d[s][v]=d[s][u]+w,q.push(make_pair(-d[s][v],v));
}
}
}
int main()
{
scanf("%d%d%d",&n,&m,&e);
for(int i=1;i<=m;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
}
for(int i=1;i<=n;i++) s=i,dij();
int ans=-1;
for(int i=1;i<=n;i++){
if(i==e) continue;
if(ans<d[i][e]+d[e][i])
ans=d[i][e]+d[e][i];
}
cout<<ans<<endl;
return 0;
}
5.Invitation Cards
原題鏈接:傳送門
思路:
- 依然是求往返路程,但這道題求的是各節點到源點的往返路程之和,而且這道題的n範圍更離譜,導致這裏不能用鄰接表存邊。
- 所以這道題應該正向和反向存圖,然後dij跑兩遍,求相加求和即可。
AC代碼如下:
// #include<bits/stdc++.h>
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#define R register int
#define mm make_pair
using namespace std;
const int manx=1000000+5;
const int mamx=manx;
struct node{
int v,next,w;
}a[2][mamx];
int d[2][manx],head[2][manx];
bool vis[manx];
int n,m,s,e,k0=0,k1=0;
void add(int i,int u,int v,int w)
{
int k;
if(i==0) k=k0; else k=k1;
a[i][++k].next=head[i][u];
head[i][u]=k;
a[i][k].v=v;
a[i][k].w=w;
if(i==0) k0=k; else k1=k;
}
void dij(int x)
{
for(int i=1;i<=n;i++) d[x][i]=1e9;
memset(vis,0,sizeof(vis));
priority_queue<pair<int,int> >q;
d[x][1]=0;
q.push(make_pair(0,1));
while(q.size()){
int u=q.top().second;
q.pop();
if(vis[u]) continue;
vis[u]=1;
for(int i=head[x][u];i;i=a[x][i].next)
{
int v=a[x][i].v,w=a[x][i].w;
if(d[x][v]>d[x][u]+w)
d[x][v]=d[x][u]+w,q.push(make_pair(-d[x][v],v));
}
}
}
int main()
{
int t;
cin>>t;
while(t--)
{
k0=0,k1=0;
memset(head,0,sizeof(head));
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(0,u,v,w);
add(1,v,u,w);
}
dij(0),dij(1);
long long ans=0;
for(int i=2;i<=n;i++){
ans+=d[0][i]+d[1][i];
}
cout<<ans<<endl;
}
return 0;
}
6.Tram
原題鏈接:傳送門
思路:
- 這道題可以說是模板題,主要是題意理解,然後按題意建邊即可。
AC代碼如下:
// #include<bits/stdc++.h>
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#define R register int
#define mm make_pair
using namespace std;
const int manx=1e3+5;
const int mamx=1e5+5;
int head[manx],d[manx];
bool vis[manx];
struct node{
int v,w,next;
}a[mamx];
priority_queue<pair<int,int> >q;
int k=0,n,m,s,e;
void add(int u,int v ,int w)
{
a[++k].next=head[u];
a[k].v=v;
a[k].w=w;
head[u]=k;
}
void dij()
{
for(int i=1;i<=n;i++) d[i]=1e9;
d[s]=0;
q.push(make_pair(0,s));
while(q.size())
{
int u=q.top().second;
q.pop();
if(vis[u]) continue;
vis[u]=1;
for(int i=head[u];i;i=a[i].next)
{
int v=a[i].v,w=a[i].w;
if(d[v]>d[u]+w&&!vis[v]) d[v]=d[u]+w,q.push(make_pair(-d[v],v));
}
}
}
int main()
{
scanf("%d%d%d",&n,&s,&e);
for(int i=1;i<=n;i++)
{
scanf("%d",&m);
for(int j=1;j<=m;j++)
{
int v;
scanf("%d",&v);
if(j==1) add(i,v,0);
else add(i,v,1);
}
}
dij();
if(d[e]==1e9) cout<<"-1"<<endl;
else cout<<d[e]<<endl;
return 0;
}
7.Wormholes
原題鏈接:傳送門
思路:
- spfa判定負環的模板題,只要有負環存在,那麼負環會越縮越小,即時間無限回溯,即可以回到出發之前。
AC代碼如下:
// #include<bits/stdc++.h>
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#define R register int
#define mm make_pair
using namespace std;
const int manx=1e3+5;
const int mamx=1e6+5;
struct node{
int v,next,w;
}a[mamx];
int d[manx],head[manx],f[manx];
bool vis[manx];
int n,m,s,e,k=0;
void add(int u,int v,int w)
{
a[++k].next=head[u];
head[u]=k;
a[k].v=v;
a[k].w=w;
}
bool spfa()
{
for(int i=1;i<=n;i++) d[i]=1e9,f[i]=0;
memset(vis,0,sizeof(vis));
queue<int>q;
d[1]=0;
f[1]=1;
q.push(1);
while(q.size()){
int u=q.front();
q.pop();
vis[u]=0;
for(int i=head[u];i;i=a[i].next)
{
int v=a[i].v,w=a[i].w;
if(d[v]>d[u]+w){
d[v]=d[u]+w;
if(!vis[v]){
f[v]++;
if(f[v]>=n) return false;
q.push(v);
vis[v]=1;
}
}
}
}
return true;
}
int main()
{
int o;
cin>>o;
while(o--){
memset(head,0,sizeof(head));
scanf("%d%d%d",&n,&m,&e);
for(int i=1;i<=m;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
for(int i=1;i<=e;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,-w);
}
if(spfa()) cout<<"NO"<<endl;
else cout<<"YES"<<endl;
}
return 0;
}
8.Cow Contest
原題鏈接:傳送門
思路:
- 給定u,v即u打敗v,求明確排名的點的個數。
- 像這種數據範圍小於1e3的題目看到第一反應就要想到floyd,即對所給邊確定 d[u][v]=1,然後通過鬆弛操作 d[i][j] = ( d[i][k] && d[k][j] ) ? 1 : 0;
- 然後對n個點進行遍歷,如果這個點跟其他n-1個點都確定關係,即這個點的排名是確定的。
AC代碼如下:
// #include<bits/stdc++.h>
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#define R register int
#define mm make_pair
using namespace std;
const int manx=1e2+5;
const int mamx=4e5+5;
struct node{
int v,next,w;
}a[mamx];
int d[manx][manx],head[manx],f[manx];
bool vis[manx];
int n,m,s,e,k=0;
void add(int u,int v,int w)
{
a[++k].next=head[u];
head[u]=k;
a[k].v=v;
a[k].w=w;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
d[u][v]=1;
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(d[i][k]&&d[k][j])
d[i][j]=1;
int ans=0;
for(int i=1;i<=n;i++)
{
int res=0;
for(int j=1;j<=n;j++)
if(d[i][j]||d[j][i]) res++;
if(res==n-1) ans++;
}
cout<<ans<<endl;
return 0;
}
9.Candies
原題鏈接:傳送門
思路:
- 給定各種約束條件,求最大差異。
- 由於約束條件中出現<= ,即求最短路 ,理解後就是模板題。
AC代碼如下:
// #include<bits/stdc++.h>
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#define R register int
#define mm make_pair
using namespace std;
const int manx=3e5+5;;
const int mamx=3e6+5;
int head[manx],d[manx];
bool vis[manx];
int n,m,k=0,s,e;
struct node{
int v,next,w;
}a[manx];
void add(int u,int v,int w)
{
a[++k].next=head[u];
a[k].w=w;
a[k].v=v;
head[u]=k;
}
void dij()
{
memset(d,0x3f,sizeof(d));
memset(vis,0,sizeof(vis));
d[s]=0;
priority_queue<pair<int,int> >q;
q.push(make_pair(0,s));
while(q.size()){
int u=q.top().second;
q.pop();
if(vis[u]) continue;
vis[u]=1;
for(int i=head[u];i;i=a[i].next){
int v=a[i].v,w=a[i].w;
if(d[v]>d[u]+w) d[v]=d[u]+w,q.push(make_pair(-d[v],v));
}
}
}
int main()
{
scanf("%d%d",&n,&m);
e=n,s=1;
for(int i=1;i<=m;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
}
dij();
cout<<d[e];
return 0;
}
10.MPI Maelstrom
原題鏈接:傳送門
思路:
- n的範圍是1e2級別的,可以考慮floyd,模板題。
- atoi(s) 是STL的一個函數,可以把一個字符數組s轉化成整型數字。
AC代碼如下:
// #include<bits/stdc++.h>
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#define R register int
#define mm make_pair
#define inf 0x3f3f3f
using namespace std;
const int manx=300;
int d[manx][manx];
int main()
{
int n,m;
char s[12];
while(scanf("%d",&n)!=EOF&&n)
{
memset(d,inf,sizeof(d));
for(int i=1;i<=n;i++) d[i][i]=0;
for(int i=2;i<=n;i++)
for(int j=1;j<i;j++)
{
cin>>s;
if(s[0]=='x') continue;
d[i][j]=d[j][i]=atoi(s);
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
int ans=-1;
for(int i=2;i<=n;i++) if(d[1][i]!=inf) ans=max(ans,d[1][i]);
cout<<ans<<endl;
}
return 0;
}
11.Layout
原題鏈接:傳送門
思路:
- 這道題可以說是差分約束系統和判斷負環的結合,可以先嚐試先做本博客的7和9題再來做這道題。
- 對於 A-B<=X; C-D>=Y;
- 把第二個式子 *(-1)即:C-D>=y -> D-C<=-y 這樣兩個式子都是小於等於方便求最短路。
AC代碼如下:
// #include<bits/stdc++.h>
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#define R register int
#define mm make_pair
using namespace std;
const int manx=1e3+5;
const int mamx=1e6+5;
struct node{
int v,next,w;
}a[mamx];
int d[manx],head[manx],f[manx];
bool vis[manx];
int n,m,s,e,k=0;
void add(int u,int v,int w)
{
a[++k].next=head[u];
head[u]=k;
a[k].v=v;
a[k].w=w;
}
void spfa()
{
for(int i=1;i<=n;i++) d[i]=1e9,f[i]=0;
memset(vis,0,sizeof(vis));
queue<int>q;
d[1]=0;
f[1]=1;
q.push(1);
while(q.size()){
int u=q.front();
q.pop();
vis[u]=0;
for(int i=head[u];i;i=a[i].next)
{
int v=a[i].v,w=a[i].w;
if(d[v]>d[u]+w){
d[v]=d[u]+w;
if(!vis[v]){
f[v]++;
if(f[v]>n){
cout<<"-1"<<endl;
return;
}
q.push(v);
vis[v]=1;
}
}
}
}
if(d[n]==1e9) cout<<"-2"<<endl;
else cout<<d[n]<<endl;
return ;
}
int main()
{
scanf("%d%d%d",&n,&m,&e);
for(int i=1;i<=m;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
for(int i=1;i<=e;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(v,u,-w);
}
spfa();
return 0;
}
12.Extended Traffic
原題鏈接:傳送門
思路:
- 這道題可以說spfa判負環的模板題。
- 但是有個坑點是用pow函數必須用(int)強轉,不然會wa ,本人實測wa5發 卡了半小時才發現是這裏出錯了。
AC代碼如下:
// #include<bits/stdc++.h>
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<stack>
#define R register int
#define mm make_pair
#define inf 0x3f3f3f3f
using namespace std;
const int manx=1e3+5;
const int mamx=1e6+5;
struct node{
int v,next,w;
}a[mamx];
int d[manx],head[manx],f[manx],ff[manx];
bool vis[manx];
int n,m,s,e,k=0;
void add(int u,int v,int w)
{
a[++k].next=head[u];
head[u]=k;
a[k].v=v;
a[k].w=w;
}
void spfa()
{
memset(ff,0,sizeof(ff));
memset(d,inf,sizeof(d));
memset(vis,0,sizeof(vis));
queue<int>q;
d[1]=0;
ff[1]=1;
q.push(1);
while(q.size()){
int u=q.front();
q.pop();
vis[u]=0;
for(int i=head[u];i;i=a[i].next)
{
int v=a[i].v,w=a[i].w;
if(d[v]>d[u]+w){
d[v]=d[u]+w;
if(!vis[v]&&ff[v]<=n){ //當ff[v]入隊次數不大於n才進入if裏面
ff[v]++;
q.push(v);
vis[v]=1;
}
}
}
}
}
int main()
{
int o,oo=1;
cin>>o;
while(o--)
{
memset(head,0,sizeof(head));
k=0;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&f[i]);
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
int u,v,w;
scanf("%d%d",&u,&v);
w=int(pow(f[v]-f[u],3));
add(u,v,w);
}
spfa();
scanf("%d",&s);
printf("Case %d:\n",oo++);
while(s--)
{
scanf("%d",&e);
if(ff[e]>n||d[e]<3||d[e]==inf) printf("?\n");
else printf("%d\n",d[e]);
}
}
return 0;
}
13.Currency Exchange
原題鏈接:傳送門
思路:
- 一種貨幣就是一個點,一個“兌換點”就是圖上兩種貨幣之間的一個兌換方式,求貨幣是否增值就是求圖中是否存在正環,即跟求負環條件相反即可,具體代碼見。
AC代碼如下:
// #include<bits/stdc++.h>
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<stack>
#define R register int
#define mm make_pair
#define inf 0x3f3f3f3f
using namespace std;
const int manx=300;
const int mamx=300;
double d[manx];
int f[manx],head[manx];
bool vis[manx];
int n,m,s,k=0;
double g;
struct node{
int v,next;
double f1,f2;
}a[mamx];
void add(int u,int v,double f1,double f2)
{
a[++k].next=head[u];
head[u]=k;
a[k].f1=f1;
a[k].f2=f2;
a[k].v=v;
}
bool spfa()
{
memset(d,0,sizeof(d));
memset(f,0,sizeof(f));
memset(vis,0,sizeof(vis));
d[s]=g;
queue<int>q;
q.push(s);
f[s]++;
while(q.size())
{
int u=q.front();
q.pop();
vis[u]=0;
for(int i=head[u];i;i=a[i].next)
{
int v=a[i].v;
double val=(d[u]-a[i].f2)*a[i].f1;
if(d[v]<val){
d[v]=val;
if(!vis[v]){
vis[v]=1;
f[v]++;
q.push(v);
if(f[v]>n) return 1;
}
}
}
}
if(d[s]>g) return 1; //即便沒有正環 看跑完起點的貨幣是否增值
return 0;
}
int main()
{
while(scanf("%d%d%d%lf",&n,&m,&s,&g)!=EOF)
{
k=0;
memset(head,0,sizeof(head));
for(int i=1;i<=m;i++)
{
int u,v;
double x,y;
scanf("%d%d",&u,&v);
scanf("%lf%lf",&x,&y);
add(u,v,x,y);
scanf("%lf%lf",&x,&y);
add(v,u,x,y);
}
if(spfa()) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
return 0;
}
14.Arbitrage
原題鏈接:傳送門
思路:
- 跟13題一樣是貨幣兌換的問題,依然求正環,由於這道題的數據規模不大,考慮floyd,在輸入的時候把數據處理好即可。
- 原本鬆弛操作: d[i][j]= min( d[i][j], d[i][k] + d[k][j])
- 本題鬆弛操作: d[i][j]= max( d[i][j], d[i][k] * d[k][j])
AC代碼如下:
// #include<bits/stdc++.h>
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<map>
#include<queue>
#include<stack>
#define R register int
#define mm make_pair
#define inf 0x3f3f3f3f
using namespace std;
const int manx=100;
int n,m;
string s;
map<string,int>q;
double d[manx][manx];
int main()
{
int haha=1;
while(scanf("%d",&n)!=EOF&&n)
{
memset(d,0.0,sizeof(d));
q.clear();
for(int i=1;i<=n;i++) d[i][i]=1.0;
for(int i=1;i<=n;i++) cin>>s,q[s]=i;
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
string u,v;
double w;
cin>>u>>w>>v;
int x,y;
x=q[u],y=q[v];
d[x][y]=max(d[x][y],w);
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
d[i][j]=max(d[i][j],d[i][k]*d[k][j]);
int flag=1;
for(int i=1;i<=n;i++)
if(d[i][i]>1)
flag=0;
if(!flag) printf("Case %d: Yes\n",haha++);
else printf("Case %d: No\n",haha++);
}
return 0;
}