SSL-1763 觀光旅遊(求無向圖的最小環問題)
目錄
題面
Description
在桑給巴爾島的Adelton城鎮上有一個旅遊機構。它們決定在提供許多的其它吸引之外,再向客人們提供旅遊本鎮的服務。 為了從提供的吸引服務中儘可能地獲利,這個旅遊機構接收了一個精明決定:在相同的起點與終點之間找出一最短路線。
Input
你的任務是編寫一條程式來找類似的的一條路線。在這個鎮上,有N個十字路口(編號1至N),兩個十字路口之間可以有多條道路連線,有M條道路(編號為1至M)。但沒有一條道路從一個十字路口出發又回到同一個路口。每一條觀光路線都是由一些路組成的,這些道路序號是:y1, …, yk,且k>2。第yi(1<=i<=k-1)號路是連線第xi號十字路口和第x[i+1]號十字路口的;其中第yk號路是連線第xk號十字路口和第x[k+1]號十字路口。而且所有的這些x1,…,xk分別代表不同路口的序號。在某一條觀光路線上所有道路的長度的和就是這條觀光路線的總長度。換言之L(y1)+L(y2)+…+L(yk)的和, L(yi)就是第yi號觀光路線的長度。你的程式必須找出類似的一條路線:長度必須最小,或者說明在這個城鎮上不存在這條觀光路線。
Output
每組資料的第一行包含兩個正整數:十字路口的個數N(N<=100),另一個是道路的 數目M(M<10000)。接下來的每一行描述一條路:每一行有三個正整數:這條路連線的兩個路口的編號,以及這條路的長度(小於500的正整數)。
Sample Input
每一行輸出都是一個答案。如果這條觀光路線是不存在的話就顯示“No solution”;或者輸出這條最短路線的長度。
Sample Output
只有一行,表示這個圖的最短路。
樣例1
5 7
1 4 1
1 3 300
3 1 10
1 2 16
2 3 100
2 5 15
5 3 20
樣例2
4 3
1 2 10
1 3 20
1 4 30
樣例1 輸出
61
樣例2 輸出
No solution
思路
圖的最小環問題,本人用了四種方法,但只有兩種A了,另外兩種還找不到錯誤,望各位大佬看看。。。
程式碼 of 30
#include<cstdio>//這是一種並查集的解法,可以過樣例,拿30分,至今沒找到其它錯誤,但仍然WA,這裡就不詳細解釋了
#include<cstring>
#define r(i,a,b) for(int i=a;i<=b;i++)
#define t(a) (a<<3)+(a<<1)+c-48
using namespace std;
int f[101],l[10001],n,m,u[101],v[101];
int w,ans=2147483647,tot,now,to,time;
struct node
{
int w,to,next;
}a[10001];
void add(int u,int v,int w)
{
a[++tot].next=l[u];
a[tot].to=v;
a[tot].w=w;
l[u]=tot;
}
int min(int x,int y){return x<y?x:y;}
int read()
{
char c;int f=0;
while((c=getchar())<48||c>57);f=t(f);
while((c=getchar())>=48&&c<=57) f=t(f);
return f;
}
int find(int x)
{
return x==f[x]?x:f[x]=find(f[x]);
}
void dfs(int x,int dep)
{
if(dep>now) return;
if(x==to&&dep) {now=dep;return;}
for(int j=l[x];j;j=a[j].next)
if(dep<now&&!(x==to&&dep>0))
{dfs(a[j].to,dep+a[j].w);time++;}
else if(time>m) break;
}
int main()
{
n=read();m=read();
r(i,1,n) f[i]=i;
r(i,1,m)
{
u[i]=read();v[i]=read();w=read();
add(u[i],v[i],w);
}
r(i,1,m)
{
int x=find(u[i]),y=find(v[i]);
if(x==y)
{
time=0;now=2147483647;to=v[i];
dfs(v[i],0);
ans=min(ans,now);
continue;
}
f[y]=x;
}
if(ans!=2147483647)
printf("%d",ans);else
puts("No sulution");
}
程式碼 of 40
#include<cstdio>//這是一種SPFA的做法,跟第一題一樣,拿不到全分,也沒有找到錯誤,其實它的思路和dij是一樣的,都是刪邊求最短路,再加距離,但只能過40分,這裡也不詳細解釋
#include<queue>
#include<vector>
#define r(i,a,b) for(int i=a;i<=b;i++)
#define t(a) (a<<3)+(a<<1)+c-48
#define INF 2147483648>>2
using namespace std;
int f[101],u[101],v[101],n,m,l[101][101],dis[101];
vector<int>a[101];
int w,ans=2147483647,tot,now,to;
int min(int x,int y){return x<y?x:y;}
int read()
{
char c;int f=0;
while((c=getchar())<48||c>57);f=t(f);
while((c=getchar())>=48&&c<=57) f=t(f);
return f;
}
int spfa(int x,int y)
{
r(j,1,n) dis[j]=INF;
dis[x]=0;
queue<int>q;bool vis[101]={0};vis[x]=true;q.push(x);
do
{
int u=q.front();q.pop();
r(i,0,a[u].size()-1)
{
int v=a[u][i],w=l[u][v];
if(dis[u]+w<dis[v])
{
dis[v]=dis[u]+w;
if(!vis[v])
{
q.push(v);
vis[v]=true;
}
}
}
vis[u]=false;
}while(!q.empty());
return dis[y];
}
int main()
{
/*freopen("first.in","r",stdin);
freopen("first.out","w",stdout);*/
n=read();m=read();
r(i,0,99) r(j,i+1,100) l[i][j]=l[j][i]=INF;
r(i,1,m)
{
u[i]=read();v[i]=read();w=read();
a[u[i]].push_back(v[i]);
a[v[i]].push_back(u[i]);
l[u[i]][v[i]]=l[v[i]][u[i]]=w;
}
r(i,1,m)
{
int t=l[u[i]][v[i]];
l[u[i]][v[i]]=l[v[i]][u[i]]=INF;
ans=min(ans,spfa(u[i],v[i])+t);
l[u[i]][v[i]]=l[v[i]][u[i]]=t;
}
if(ans>=50000000)
puts("No solution");else printf("%d",ans);
}
程式碼 of dijkstra
#include<cstdio>//這個效率要低於floyed,因為這張圖比較密集,列舉邊的時間更長了。
#define INF 2147483647>>2
#define N 101
#define r(i,a,b) for(int i=a;i<=b;i++)
#define t(a) (a<<3)+(a<<1)+c-48
using namespace std;
int dis[N],l[N][N],n,ans=INF,m,x[N*N],y[N*N],w[N*N];
int min(int x,int y){return x<y?x:y;}
int read()
{
char c;int f=0;
while((c=getchar())<48||c>57);f=t(f);
while((c=getchar())>=48&&c<=57) f=t(f);
return f;
}
int dijkstra(int u,int v)//dij不解釋
{
bool vis[N]={0};int minn=0,k=0;
for(int i=1;i<=n;i++)
dis[i]=l[u][i];
vis[u]=true;dis[u]=0;
r(i,1,n-1)
{
minn=INF;
k=0;
r(j,1,n)
if(!vis[j]&&dis[j]<minn)
{
minn=dis[j];
k=j;
}
if(!k) break;
vis[k]=true;
r(j,1,n)
if(dis[k]+l[k][j]<dis[j])
dis[j]=dis[k]+l[k][j];
}
return dis[v];
}
int main()
{
n=read();m=read();
r(i,0,N-1)
r(j,0,N-1)
l[i][j]=INF;
r(i,1,m)
{
x[i]=read();y[i]=read();w[i]=read();
l[x[i]][y[i]]=l[y[i]][x[i]]=w[i];//初始化
}
r(i,1,m)
{
int t=l[x[i]][y[i]]=l[y[i]][x[i]];
l[x[i]][y[i]]=l[y[i]][x[i]]=INF;//刪邊
ans=min(ans,dijkstra(x[i],y[i])+t);//計算
l[x[i]][y[i]]=l[y[i]][x[i]]=t;//復邊
}
if(ans>=50000000) puts("No solution");else
printf("%d",ans);//特判
}
程式碼 of floyed
#include<cstdio>//這個程式碼最短的反而AC了,十分神奇
#include<cstring>
#define INF 2147483647>>2
#define N 101
#define r(i,a,b) for(int i=a;i<=b;i++)
#define t(a) (a<<3)+(a<<1)+c-48
using namespace std;
int ans=INF,l[N][N],n,m,x,y,w,dis[N][N];
int min(int x,int y){return x<y?x:y;}
int read()
{
char c;int f=0;
while((c=getchar())<48||c>57);f=t(f);
while((c=getchar())>=48&&c<=57) f=t(f);
return f;
}
int main()
{
n=read();m=read();
memset(dis,127>>2,sizeof(dis));
memset(l,127>>2,sizeof(l));
r(i,1,m)
{
x=read();y=read();w=read();
l[x][y]=l[y][x]=w;
dis[x][y]=dis[y][x]=w;//初始化
}
r(k,1,n)
{
r(i,1,n)
r(j,i+1,n)
ans=min(ans,dis[i][j]+l[i][k]+l[k][j]);//求答案,當然這裡的n改成k也可以AC,只不過時間複雜度不一樣罷了
r(i,1,n)
r(j,1,n)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);//floyed
}
if(ans>=50000000) puts("No solution");else
printf("%d",ans);//特判
}