zoukankan      html  css  js  c++  java
  • sdut3562-求字典序最小的最短路 按顶点排序后spfa的反例

    首先我们可以这么搞...倒序建图,算出源点s附近的点距离终点的距离,然后判断一下,终点是否能跑到源点

    能跑到的话呢,我们就判断s周围的点是否在最短路上,然后我们选编号最小的点就好了

    代码

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <queue>
    #include <vector>
    #include <cstring>
    using namespace std;
    const int maxn=1005;
    const int INF=1e9;
    int n,m,dist[maxn],pre[maxn];bool vis[maxn];
    struct node{
        int v,w;node(){};node(int v,int w):v(v),w(w){};
    };
    bool cmp(node a,node b){
        return a.v<b.v;
    }
    vector<node> G[maxn];
    vector<node> St;
    void init(){
        for(int i=0;i<maxn;++i) G[i].clear();St.clear();
        for(int i=0;i<maxn;++i) dist[i]=INF;
        memset(vis,0,sizeof(vis));
        memset(pre,-1,sizeof(pre));
    }
    int main(){
        int T;scanf("%d",&T);
        while(T--){
            scanf("%d%d",&n,&m);
            init();int a,b,c;
            for(int i=0;i<m;++i){
                scanf("%d%d%d",&a,&b,&c);
                G[b].push_back(node(a,c));
                if(a==0) St.push_back(node(b,c));
                //G[b].push_back(node(a,c));
            }
            queue<int> Q;Q.push(n+1);vis[n+1]=1;dist[n+1]=0;
            while(Q.size()){
                int now=Q.front();Q.pop();vis[now]=0;
                for(int i=0;i<G[now].size();++i){
                    int to=G[now][i].v,tw=G[now][i].w;
                    if(dist[to]>dist[now]+tw){
                        dist[to]=dist[now]+tw;
                        pre[to]=now;
                        if(!vis[to]) {
                            Q.push(to);vis[to]=1;
                        }
                    }
                }
            }
            if(dist[0]==INF) {
                printf("-1
    ");continue;
            }
            int flag=0,ans=INF;
            for(int i=0;i<St.size();++i){
                int to=St[i].v,tw=St[i].w;
                if(tw+dist[to]==dist[0]){
                    if(to==n+1){
                        flag=1;break;
                    }
                    ans=min(ans,to);
                }
            }
            if(flag) printf("0
    ");
            else      printf("%d
    ",ans);
        }
        return 0;
    }

    下面这种做法是错误做法,那就是先对每个邻接表按顶点标号大小排序,然后跑一遍spfa

    一般的数据都能正常出解,但是对于这一组数据,哈哈,怕是翻车了

    2 4
    0 1 2
    1 2 1
    0 2 3
    2 3 1

    松弛完1,直接松弛0,2这条边,于是0,1,2,3这条路径不会被考虑了,

    于是答案就是2,而不是正确的1,1被早来的0,2这条边从后面架空了

    附上错误代码

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <queue>
    #include <vector>
    #include <cstring>
    using namespace std;
    const int maxn=1005;
    const int INF=1e9;
    int n,m,dist[maxn],pre[maxn];bool vis[maxn];
    struct node{
        int v,w;node(){};node(int v,int w):v(v),w(w){};
    };
    bool cmp(node a,node b){
        return a.v<b.v;
    }
    vector<node> G[maxn];
    void init(){
        for(int i=0;i<maxn;++i) G[i].clear();
        for(int i=0;i<maxn;++i) dist[i]=INF;
        memset(vis,0,sizeof(vis));
        memset(pre,-1,sizeof(pre));
    }
    int main(){
        int T;scanf("%d",&T);
        while(T--){
            scanf("%d%d",&n,&m);
            init();int a,b,c;
            for(int i=0;i<m;++i){
                scanf("%d%d%d",&a,&b,&c);
                G[a].push_back(node(b,c));
                //G[b].push_back(node(a,c));
            }
            for(int i=0;i<maxn;++i){
                if(G[i].size()) sort(G[i].begin(),G[i].end(),cmp);
            }
            queue<int> Q;Q.push(0);vis[0]=1;dist[0]=0;
            while(Q.size()){
                int now=Q.front();Q.pop();vis[now]=0;
                for(int i=0;i<G[now].size();++i){
                    int to=G[now][i].v,tw=G[now][i].w;
                    if(dist[to]>dist[now]+tw){
                        dist[to]=dist[now]+tw;
                        pre[to]=now;
                        if(!vis[to]) {
                            Q.push(to);vis[to]=1;
                        }
                    }
                }
            }
            if(dist[n+1]==INF) {
                printf("-1
    ");continue;
            }
            int i;
            for(i=0;i<G[0].size();++i) {
                if(G[0][i].v==n+1&&G[0][i].w==dist[n+1]) break;
            }
            if(i<G[0].size()) {printf("0
    ");continue;}
            for(i=n+1;pre[i]!=0;i=pre[i]);
            printf("%d
    ",i);
        }
        return 0;
    }

     由于路径不算原点,所以说

    0 2 3>0 1 2 3

    也就是 2 3>1 2 3

    所以在源点需要特判

    然后还有一个要注意地方

    有一种因为数据弱而能ac的错误做法,在求最短路的过程中,尝试使它的前驱变小

    这样做的缺陷在于,你没法保留当前前驱大但是字典序小的路径,这种做法的代码如下

    #include<iostream>
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<vector>
    #include<utility>
    #include<queue>
    using namespace std;
    #define mp make_pair
    #define X first
    #define Y second
    const int N=1008;
    const int M=1000000008;
    int n,m;
    vector<pair<int,int> >a[N];
    int b[N],c[N],flag[N];
    queue<int>d;
    int main(void)
    {
        int t,i,k,p1,p2,p3;
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d%d",&n,&m);
            for(i=0;i<=n+1;i++)a[i].clear();
            while(m--){scanf("%d%d%d",&p1,&p2,&p3);a[p1].push_back(mp(p2,p3));}
            for(i=0;i<=n+1;i++){b[i]=c[i]=M;flag[i]=0;}
            while(!d.empty()){d.pop();}d.push(0);b[0]=c[0]=0;flag[0]=1;
            while(!d.empty())
            {
                k=d.front();d.pop();flag[k]=0;
                for(i=0;i<(int)a[k].size();i++)
                if((b[a[k][i].X]>b[k]+a[k][i].Y)||(b[a[k][i].X]==b[k]+a[k][i].Y&&c[a[k][i].X]>k))
                {
                    b[a[k][i].X]=b[k]+a[k][i].Y;
                    c[a[k][i].X]=k;
                    if(flag[a[k][i].X]==0){flag[a[k][i].X]=1;d.push(a[k][i].X);}
                }
            }
            if(b[n+1]==M){printf("-1
    ");continue;}
            for(i=0;i<(int)a[0].size();i++)if(a[0][i].X==n+1&&a[0][i].Y==b[n+1])break;
            if(i<(int)a[0].size()){printf("0
    ");continue;}
            k=n+1;while(c[k])k=c[k];
            printf("%d
    ",k);
        }
        return 0;
    }

    除了逆向建图的做法之外,我们可以正向先跑出一个最短距离

    然后把s的邻接点全部按编号排序,从s的邻接点挨个跑一遍s'到t的最短路,看该点在不在最短路上

    找到的第一个解就是答案,附上代码

    #include<iostream>
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<vector>
    #include<utility>
    #include<queue>
    #include<algorithm>
    using namespace std;
    #define mp make_pair
    #define X first
    #define Y second
    const int N=1008;
    const int M=1000000008;
    int n,m;
    vector<pair<int,int> >a[N];
    int b[N],flag[N];
    queue<int>d;
    int main(void)
    {
        int t,i,k,p1,p2,p3;
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d%d",&n,&m);
            for(i=0;i<=n+1;i++)a[i].clear();
            while(m--){scanf("%d%d%d",&p1,&p2,&p3);a[p1].push_back(mp(p2,p3));}
            for(i=0;i<=n+1;i++){b[i]=M;flag[i]=0;}
            while(!d.empty()){d.pop();}d.push(0);b[0]=0;flag[0]=1;
            while(!d.empty())
            {
                k=d.front();d.pop();flag[k]=0;
                for(i=0;i<(int)a[k].size();i++)if(b[a[k][i].X]>b[k]+a[k][i].Y)
                {
                    b[a[k][i].X]=b[k]+a[k][i].Y;
                    if(flag[a[k][i].X]==0){flag[a[k][i].X]=1;d.push(a[k][i].X);}
                }
            }
            p3=b[n+1];
            if(b[n+1]==M){printf("-1
    ");continue;}
            for(i=0;i<(int)a[0].size();i++)if(a[0][i].X==n+1&&a[0][i].Y==b[n+1])break;
            if(i<(int)a[0].size()){printf("0
    ");continue;}
            sort(a[0].begin(),a[0].end());
            for(p1=0;p1<(int)a[0].size();p1++)
            {
                for(i=0;i<=n+1;i++){b[i]=M;flag[i]=0;}
                while(!d.empty()){d.pop();}d.push(a[0][p1].X);b[a[0][p1].X]=0;flag[a[0][p1].X]=1;
                while(!d.empty())
                {
                    k=d.front();d.pop();flag[k]=0;
                    for(i=0;i<(int)a[k].size();i++)if(b[a[k][i].X]>b[k]+a[k][i].Y)
                    {
                        b[a[k][i].X]=b[k]+a[k][i].Y;
                        if(flag[a[k][i].X]==0){flag[a[k][i].X]=1;d.push(a[k][i].X);}
                    }
                }
                if(b[n+1]+a[0][p1].Y==p3)break;
            }
            printf("%d
    ",a[0][p1].X);
        }
        return 0;
    }
  • 相关阅读:
    db2 load命令装载数据时定位错误出现的位置
    DB2 SQL error: SQLCODE: -668, SQLSTATE: 57016, SQLERRMC: 3
    db2重组所有表和更新表统计信息
    DB2消息
    db2 SQL查询路径
    db2备份与恢复
    博弈-题表
    【POJ1082】Calendar Game (博弈)
    【POJ1067】取石子游戏 (威佐夫博弈)
    【POJ3710】Christmas Game (博弈-树上的删边问题)
  • 原文地址:https://www.cnblogs.com/linkzijun/p/6785064.html
Copyright © 2011-2022 走看看