1. 程式人生 > >HDU-2433 Travel(最短路[Dijkstra])

HDU-2433 Travel(最短路[Dijkstra])

Travel

Time Limit: 10000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)


Problem Description       One day, Tom traveled to a country named BGM. BGM is a small country, but there are N (N <= 100) towns in it. Each town products one kind of food, the food will be transported to all the towns. In addition, the trucks will always take the shortest way. There are M (M <= 3000) two-way roads connecting the towns, and the length of the road is 1.
      Let SUM be the total distance of the shortest paths between all pairs of the towns. Please write a program to calculate the new SUM after one of the M roads is destroyed.


Input       The input contains several test cases.
      The first line contains two positive integers N, M. The following M lines each contains two integers u, v, meaning there is a two-way road between town u and v. The roads are numbered from 1 to M according to the order of the input.
      The input will be terminated by EOF.


Output       Output M lines, the i-th line is the new SUM after the i-th road is destroyed. If the towns are not connected after the i-th road is destroyed, please output “INF” in the i-th line. 

Sample Input 5 4 5 1 1 3 3 2 5 4 2 2 1 2 1 2
Sample Output INF INF INF INF 2 2

題目大意:多組輸入,每組第一行讀入定點數n和邊數m,接下來m行是邊。輸出1~m條邊分別摧毀後任意兩點(分起點終點)的最短距離和,如果存在兩點不連通,則INF

由於邊數多,如果每次都計算任意兩點的距離會超時。

看到大神的思路後,注意到每次只刪除一條邊,如果這條邊在某兩點(u,v)的最短路徑中,則u,v兩點的最短路長度會變,其他不變

但是太懶了,不想記錄路徑,又看到很多人直接只算(u,v)的最短路,然後減去原來u,v的最短路*2,加上最新的最短路*2就是答案,但這樣的程式碼居然能AC,資料真是好水

例如這組資料就會掛很多人

4 4 
1 2
2 3
3 4 
2 4

答案:
INF
20
18
20

按照正確思路寫出來,TLE,可能是太渣了,而且每對頂點都跑一邊最短路徑

又看到大神的另一種思路:used[x][u][v] 來記錄對於以x為源點的最短路徑樹,是否使用w[u][v] 這條路,如果使用則重新計算一遍最短路

,這樣就能得到正確結果

#include <cstdio>
#include <cstring>

using namespace std;

const int INF=0x3f3f3f3f;
const int MAXN=105;
int w[MAXN][MAXN],dis[MAXN][MAXN],dist[MAXN],num[MAXN][MAXN],sta[3005],des[3005];
int n,m,s,e,u,tmp,sum,ans;
bool vis[MAXN],used[MAXN][MAXN][MAXN];

void Dijkstra() {
    int i,j;
    for(i=1;i<=n;++i) {
        tmp=INF,e=0;
        for(j=1;j<=n;++j)
            if(!vis[j]&&dist[j]<tmp)
                tmp=dist[u=j];
        if(tmp==INF)
            return ;
        vis[u]=true;
        for(j=1;j<=n;++j)
            if(!vis[j]&&(tmp=dist[u]+w[u][j])<dist[j])
                dist[j]=tmp;
    }
}

void DijkstraInit() {
    int i,j;
    for(i=1;i<=n;++i) {
        tmp=INF;
        for(j=1;j<=n;++j)
            if(!vis[j]&&dis[s][j]<tmp)
                tmp=dis[s][u=j];
        if(tmp==INF)
            return ;
        vis[u]=true;
        for(j=1;j<=n;++j)
            if(!vis[j]&&(tmp=dis[s][u]+w[u][j])<dis[s][j]) {
                dis[s][j]=tmp;
                used[s][u][j]=used[s][j][u]=true;
            }
    }
}

void Judge(int i) {
    int j,k;
    for(j=1;j<=n;++j) {
        if(used[j][sta[i]][des[i]]) {
            memset(vis,false,sizeof(vis));
            memset(dist,0x3f,sizeof(dist));
            dist[j]=0;
            Dijkstra();
            for(k=1;k<=n;++k)
                if(dist[k]==INF) {
                    ans=INF;
                    return ;
                }
                else
                    ans+=(dist[k]-dis[j][k])<<1;
        }
    }
}

int main() {
    int i,j;
    while(scanf("%d%d",&n,&m)==2) {
        memset(w,0x3f,sizeof(w));
        memset(dis,0x3f,sizeof(dis));//剛開始忘了初始化dis,結果差不多能跑到最後一組資料,一直以為是演算法寫錯了。。。
        memset(num,0,sizeof(num));
        memset(used,false,sizeof(used));
        for(i=0;i<m;++i) {
            scanf("%d%d",&s,&e);
            dis[sta[i]=s][des[i]=e]=dis[e][s]=w[s][e]=w[e][s]=1;
            ++num[s][e],++num[e][s];//記錄邊出現的次數
            used[s][s][e]=true;//由於dis[s][e]已經初始化,且權值為1,所以(s,e)一定會出現在以s為源點的最短路徑樹上
        }
        sum=0;
        for(i=1;i<=n;++i) {
            s=i,dis[i][i]=0;
            memset(vis,false,sizeof(vis));
            DijkstraInit();
            for(j=1;j<i;++j)
                sum+=(dis[i][j]<<1);//根據題意得圖是連通的
        }
        for(i=0;i<m;++i) {
            if(num[sta[i]][des[i]]>1)//特判,如果該邊出現的次數>1就直接輸出結果
                printf("%d\n",sum);
            else {
                w[sta[i]][des[i]]=w[des[i]][sta[i]]=INF;
                ans=sum;
                Judge(i);
                if(ans!=INF)
                    printf("%d\n",ans);
                else
                    printf("INF\n");
                w[sta[i]][des[i]]=w[des[i]][sta[i]]=1;
            }
        }
    }
    return 0;
}