1. 程式人生 > >最短路計數 && 路經統計

最短路計數 && 路經統計

兩道題基本上一樣的,但不同的是路經統計資料更強一些,考慮的東西一應該更多一些,兩道題目都問的是最短路的條數;
先說最短路計數,我先做的這道題目,最初想的是在spfa的過程中進行記錄但沒有想出來實現方法,所以改成了spfa + dfs(),spfa 先跑出最短路,然後dfs再搜一遍全圖,去記錄到達每個點的條數,但是後兩個點TLE了;


#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<iomanip>
#include<queue>
#include<cstring>
using namespace std;
inline int read(){
    int x = 0;int f = 1; char c = getchar();
    while(c<'0'||c>'9'){
        if(c == '-')f = -f;
        c = getchar();
    }
    while(c<='9'&&c>='0'){
        x = x*10 + c - '0';
        c = getchar();
    }
    return x*f;
}
const int maxn = 1000000;
queue<int>que;
struct edge{
    int from,to,v;
}e[maxn * 4];
int n,m,p,flag,ans;
int head[maxn * 4],record[maxn * 4];
int dis[maxn],vis[maxn];
void add(int a,int b){
    p++;
    e[p].from = head[a];
    e[p].to = b;
    head[a] = p;
}
void spfa(){
    for(int i = 1; i<=n; i++)dis[i] = maxn;
    memset(vis,0,sizeof(vis));
    que.push(1);
    dis[1] = 0;
    vis[1] = 1;
    while(!que.empty()){
        int f = que.front();que.pop();
        vis[f] = 0;
        for(int i = head[f] ; i ; i = e[i].from){
            int u = e[i].to;
            if(dis[u] > dis[f] + 1){
                dis[u] = dis[f] + 1;
                if(!vis[u]){
                    vis[u] = 1;
                    que.push(u);
                }
            }
        }
    }
}
void dfs(int root,int t){
    if(t > dis[root])return;
    else record[root]++;
    for(int i = head[root] ; i ; i = e[i].from){
        if(vis[e[i].to]||t + 1 > dis[e[i].to])continue;
        vis[root] = 1;
        dfs(e[i].to,t + 1);
    }
    vis[root] = 0;
}
int main(){
    n = read();m = read();
    for(int i = 1; i<=m; i++){
        int u,v;
        u = read();
        v = read();
        add(u,v);
        add(v,u);
    }
    spfa();
    dfs(1,0);
    for(int i = 1; i<=n; i++){
        printf("%d\n",record[i]);
    }
    return 0;
}

這是60分的程式碼後兩個點會TLE;
後來看了題解發現題解都是在跑spfa的過程中來維護的,當一個位置最短路被更新時到達它的條數就是之前那個點的最短路的條數,如果相等的話說明這條路徑與此時的最短路相等,所以到達該點的最短路就是轉移來的點的最短路條數 + 該點已經計算過的最短路條數。所以完全可以去掉dfs,在spfa中這樣寫

 if(dis[u] > dis[f] + 1){
                dis[u] = dis[f] + 1;
                record[u] = record[f];//記錄的條數
                if(!vis[u]){
                    vis[u] = 1;
                    que.push(u);
                }
            }
            else if(dis[u] == dis[f] + 1){
                record[u] = (record[u] + record[f])% 100003;
            }

記得初始化!!!record[1] = 1;

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
inline int read(){
    int x = 0;int f = 1;char c = getchar();
    while(c<'0'||c>'9'){
        if(c == '-')f = -f;
        c = getchar(); 
    }
    while(c<='9'&&c>='0'){
        x = x*10 + c - '0';
        c = getchar(); 
    }
    return x*f;
}
queue<int>que;
queue<int>q;
int p,n,m;
const int maxn = 200000;
struct edge{
    int from,to,v;
}e[maxn * 20];
int head[maxn * 20],dis[maxn * 40];
void add(int a,int b){
    p++;
    e[p].from = head[a];
    e[p].to = b;
    head[a] = p;    
}
int record[maxn * 20],vis[maxn * 40];
void bfs(int root,int t){
    for(int i = head[root] ; i ; i = e[i].from ){
        int u = e[i].to;
        if(t + 1 < dis[u]){
            dis[u] = t + 1;
            
        } 
    } 
}
void spfa(){
    for(int i = 1; i<=n; i++)dis[i] = maxn ;
    memset(vis,0,sizeof(vis));
    que.push(1);
    vis[1] = 1;
    dis[1] = 0;
    while(!que.empty()){
        int f = que.front();que.pop();
        vis[f] = 0;
        for(int i = head[f] ; i ; i = e[i].from){
            int u = e[i].to;
            if(dis[u] > dis[f] + 1){
                dis[u] = dis[f] + 1;
                record[u] = record[f];
                if(!vis[u]){
                    vis[u] = 1;
                    que.push(u);
                }
            }
            else if(dis[u] == dis[f] + 1){
                record[u] = (record[u] + record[f])% 100003;
            }
        }
    }
}
int main(){
    n = read(); m = read();
    for(int i = 1; i<=m; i++){
        int u,v;
        u = read();
        v = read();
        add(u,v);
        add(v,u); 
    }
    record[1] = 1;
    spfa();
    for(int i = 1; i<=n; i++)cout << record[i] << '\n';
    return 0;
} 

這樣寫的話其實有點小錯誤的:相等的點並不會判斷是否入隊,並且record[i]沒有清零(原因見下面的一組資料),這樣就會在路經統計那道題裡面被卡掉;

5 5
1 2 1
2 3 1
3 4 1
4 5 1
1 4 3
(sxy dalao的解釋)
如果不清零4會入隊兩次, 第一次是1->4 (v=3,dis=3,path=1)

然後4->5 (v=1,dis=4,path=1)

第二次是3->4(v=1,dis=3,path=1+1)

然後4->5(v=1,dis=4,path=1+2)

然後就會輸出

4 3
實際是

4 2

所以在路經統計這道題目中把spfa中的東西分開寫,最後統一判斷是否入隊;同時因為點數並不是很大,可以用鄰接矩陣存下來每兩個點之間的最短距離,然後spfa解決;

#include<iostream>
#include<cstdio>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
inline int read(){
    int x = 0;int f = 1; char c = getchar();
    while(c<'0'||c>'9'){
        if(c == '-')f = -f;
        c = getchar();
    }
    while(c<='9'&&c>='0'){
        x = x*10 + c - '0';
        c = getchar();
    }
    return x*f;
}
int n,e;
int pic[3000][3000];
int dis[30000],vis[30000],record[30000];
queue<int>que;
void spfa(){
    for(int i = 1; i<=n; i++)dis[i] = 20000;

    memset(vis,0,sizeof(vis));
    que.push(1);
    dis[1] = 0;
    vis[1] = 1;
    while(!que.empty()){
        int f = que.front();que.pop();
        if(f == n)continue;
        vis[f] = 0;
        for(int i = 1; i<=n; i++){
            if(pic[f][i] == 20000)continue;
            
             if(dis[i] > dis[f] + pic[f][i]){
                dis[i] = dis[f] + pic[f][i];
                record[i] = record[f];
            }
             
            else if(dis[i] == dis[f] + pic[f][i]){
                record[i] = record[i] + record[f];
             }
              if(!vis[i]&&record[i]){
                    que.push(i);
                    vis[i] = 1;
                 }
                    
        }
        record[f] = 0;
    }
}
int main(){
    n = read(); e = read();
    for(int i = 1 ; i<=n; i++){
        for(int j = 1; j<=n; j++){
            pic[i][j] = 20000;
        }
    }
    for(int i = 1 ; i<=e; i++){
        int a,b,c;
        a = read();
        b = read();
        c = read();
        pic[a][b] = min(pic[a][b],c);
    }
    record[1] = 1; 
    spfa();
    if(dis[n] == 20000)cout << "No answer";
    else cout << dis[n] <<' '<< record[n];
    
    return 0;
}