1. 程式人生 > >關於圖論中最短路徑的五道題-ACM

關於圖論中最短路徑的五道題-ACM

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題要有快速建模的意識!