最短路计数 && 路经统计

两道题基本上一样的,但不同的是路经统计数据更强一些,考虑的东西一应该更多一些,两道题目都问的是最短路的条数;
先说最短路计数,我先做的这道题目,最初想的是在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;
}

猜你喜欢

转载自www.cnblogs.com/Euplectella/p/9929373.html