關於圖論中最短路徑的五道題-ACM
阿新 • • 發佈:2019-01-09
ACM隊內訓練第六週的專題是圖論中最短路徑問題,今晚終於把五道題目都AC了,下面依次總結一下並奉上程式碼。
題目列表
NO.1 poj1125 Stockbroker Grapevine
題目給定一個有向圖,求解最短路徑,要求輸出作為源的頂點座標以及該頂點到達某個頂點的最大權值。資料量100,使用Floyd演算法,複雜度為n^3。無特殊處理。
/*最短路徑演算法-Floyd演算法*/
#include <iostream>
#include<cstring>
#include<stdio.h>
#include<cstdlib>
#include<cmath>
#include<string>
#include<vector>
#include<list>
#include<map>
#include<queue>
#include<stack>
#include<algorithm>
using namespace std;
#define maxn 0x3f3f3f3f
struct Graph{ //鄰接矩陣表示法
int arcs[105][105];
}g;
struct ShortPath{
int a[105][105];
}path;
void floyd(int n){
for(int i=0 ; i<n ; i++) //初始化
for(int j=0 ; j<n ; j++)
path.a[i][j] = g.arcs[i][j];
for(int k=0 ; k<n ; k++)
for(int i=0 ; i<n ; i++)
for(int j=0 ; j<n ; j++){
if(path.a[i][k]>=maxn || path.a[k][j]>=maxn)
continue ;
if(path.a[i][j] > (path.a[i][k] + path.a[k][j]))
path.a[i][j] = path.a[i][k] + path.a[k][j];
}
}
int main()
{
//freopen("in.txt","r",stdin);
int n;
while(cin>>n && n!=0){
int m,j;
memset(g.arcs,maxn,sizeof(g.arcs));
for(int i=0 ; i<n ; i++)
g.arcs[i][i]=0;
for(int i=0 ; i<n ; i++){
cin>>m;
while(m--){
cin>>j;
cin>>g.arcs[i][j-1];
}
}
floyd(n);
int shortpath=maxn,pathlen,point=0;
for(int i=0 ; i<n ; i++){
bool flag=true;
pathlen=0;
for(int j=0 ; j<n ; j++){
if(path.a[i][j]>=maxn){
flag=false;
break;
}
pathlen += path.a[i][j];
}
if(!flag)
continue;
if(shortpath>pathlen){
shortpath=pathlen;
point=i;
}
}
if(shortpath>=maxn)
cout<<"disjoint"<<endl;
else{
cout<<point+1<<" ";
sort(path.a[point],path.a[point]+n);
cout<<path.a[point][n-1]<<endl;
}
}
return 0;
}
NO.2 poj3615 Cow Hurdles
題目要求最短路徑中任意兩點的最大距離,採用Floyd演算法,直接把二維陣列用作鄰接矩陣,WA了兩次,原因是輸入時需注意從一根柱子跳到另一根柱子是不可逆的,也就是說a[x][y]!=a[y][x]。
#include <iostream>
#include<cstring>
#include<stdio.h>
#include<cstdlib>
#include<cmath>
#include<string>
#include<vector>
#include<list>
#include<map>
#include<queue>
#include<stack>
#include<algorithm>
using namespace std;
#define maxn 0x3f3f3f3f
int a[305][305];
void floyd(int n){
for(int k=1 ; k<=n ; k++)
for(int i=1 ; i<=n ; i++)
for(int j=1 ; j<=n ; j++)
a[i][j] = min(a[i][j],max(a[i][k],a[k][j]));
}
int main(){
int n,m,t;
while(scanf("%d%d%d",&n,&m,&t)!=EOF){
int x,y,z;
// memset(a,maxn,sizeof(a));
for(int i=1 ; i<=n ; i++)
for(int j=1 ; j<=n ; j++)
a[i][j]=maxn;
while(m--){
//cin>>x>>y;
scanf("%d%d%d",&x,&y,&z);
a[x][y]=z;
//a[y][x]=a[x][y];錯誤,需留意
}
floyd(n);
while(t--){
scanf("%d%d",&x,&y);
if(a[x][y]>=maxn)
printf("-1\n");
else
printf("%d\n",a[x][y]);
}
}
return 0;
}
NO.3 poj1847 Tram
題目意思是說有n個點,從A點到其他點,若可達且是該行輸入的第一個點則權值為0,若可達且不是第一個點則權值為1,否則不可達。由於資料量不大,使用Floyd演算法求解。
#include <iostream>
#include<cstring>
#include<stdio.h>
#include<cstdlib>
#include<cmath>
#include<string>
#include<vector>
#include<list>
#include<map>
#include<queue>
#include<stack>
#include<algorithm>
using namespace std;
#define maxn 0x3f3f3f3f
int point[105][105];
int n,a,b;
void floyd(){
for(int q=1 ; q<=n ; q++)
for(int i=1 ; i<=n ; i++)
for(int j=1 ; j<=n ; j++){
if(point[i][j]>(point[i][q]+point[q][j]))
point[i][j] = point[i][q]+point[q][j];
}
}
int main(){
while(~scanf("%d%d%d",&n,&a,&b)){
int m,k;
for(int i=1 ; i<=n ; i++){ //初始化
for(int j=1 ; j<=n ; j++){
if(i==j)
point[i][j]=0;
else
point[i][j]=maxn;
}
}
for(int i=1 ; i<=n ; i++){
scanf("%d",&m);
for(int j=0 ; j<m ; j++){
scanf("%d",&k);
if(j==0)
point[i][k]=0;
else
point[i][k]=1;
}
}
floyd();
if(point[a][b]>=maxn)
cout<<-1<<endl;
else
cout<<point[a][b]<<endl;
}
return 0;
}
NO.4 poj1502 MPI Maelstrom
無向圖求最短路徑,無特殊要求,直接使用Dijkstra演算法求解,複雜度為n^2。
/*Dijkstra演算法-最短路*/
#include<iostream>
#include<cstring>
#include<stdio.h>
#include<cstdlib>
#include<cmath>
#include<string>
#include<vector>
#include<list>
#include<map>
#include<queue>
#include<stack>
#include<algorithm>
using namespace std;
#define max 0x3f3f3f3f
struct Graph{ //鄰接矩陣表示法
int arcs[105][105];
}g;
int dist[105]; //儲存最短路徑
void init(Graph pg,int dist[],int n){
dist[0] = 0;
pg.arcs[0][0] = 1; //若對角線元素為1,則表示該頂點已被使用
for(int i=1 ; i<n ; i++)
dist[i] = pg.arcs[0][i];
}
void dijkstra(Graph g,int dist[],int n){
int mv,minw;
init(g,dist,n); //初始化
for(int i=1 ; i<n ; i++){
minw=max;
mv=0;
for(int j=1 ; j<n ; j++) //選出距離v0最近的頂點
if(g.arcs[j][j]==0 && dist[j]<minw){
mv=j;
minw=dist[j];
}
if(mv == 0) break; //v0與vi不連通,結束
g.arcs[mv][mv] = 1;
for(int j=1 ; j<n ; j++){
if(g.arcs[j][j]==0 && dist[j] >dist[mv]+g.arcs[mv][j]){
dist[j] = dist[mv]+g.arcs[mv][j];
}
}
}
}
int main(){
int n;
while(cin>>n){
memset(g.arcs,0,sizeof(g.arcs));
string input;
for(int i=1 ; i<n ; i++){
for(int j=0 ; j<i ; j++){
cin>>input;
if(input=="x"){
g.arcs[i][j]=max;
g.arcs[j][i]=max;
}else{
g.arcs[i][j]=atoi(input.c_str());
g.arcs[j][i]=atoi(input.c_str());
}
}
}
dijkstra(g,dist,n);
sort(dist,dist+n);
cout<<dist[n-1]<<endl;
}
return 0;
}
NO.5 poj1860 Currency Exchange
Bellman_Ford演算法求解有向帶權圖是否存在正負環。陣列開小導致RE,遍歷的時候注意起點,輸入的變數較多,順序容易搞錯。
#include <iostream>
#include<cstring>
#include<stdio.h>
#include<cstdlib>
#include<cmath>
#include<string>
#include<vector>
#include<list>
#include<map>
#include<queue>
#include<stack>
#include<algorithm>
using namespace std;
struct Edge{
int u,v;
double rate,cost;
};
Edge edge[1005];
double dist[5005];
double ss;
int start,count1;
bool Bellman_Ford(int n){
memset(dist,0,sizeof(dist));
dist[start]=ss;
bool flag;
for(int i=1 ; i<n ; i++){
flag=false;
for(int j=0 ; j<count1 ; j++)
if(dist[edge[j].v] < (dist[edge[j].u]-edge[j].cost)*edge[j].rate){
dist[edge[j].v] = (dist[edge[j].u]-edge[j].cost)*edge[j].rate;
flag = true;
}
if(!flag)
break;
}
for(int j=0 ; j<count1 ; j++)
if(dist[edge[j].v] < (dist[edge[j].u]-edge[j].cost)*edge[j].rate)
return true;
return false;
}
int main(){
int n,m;
while(cin>>n>>m>>start>>ss){
count1=0;
int uu,vv;
double p1,p2,p3,p4;
while(m--){
cin>>uu>>vv>>p1>>p2>>p3>>p4;
edge[count1].u=uu;
edge[count1].v=vv;
edge[count1].rate=p1;
edge[count1].cost=p2;
count1++;
edge[count1].u=vv;
edge[count1].v=uu;
edge[count1].rate=p3;
edge[count1].cost=p4;
count1++;
}
if(Bellman_Ford(n))
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}
return 0;
}
結語
圖論的題目接觸不多,一開始寫的程式碼有些贅餘,後來發現後慢慢改正一些。感受最深的一處是:資料量不大的情況下,直接用二維陣列表示圖的鄰接矩陣比較方便。以後做ACM題要有快速建模的意識!