• 最短路习题题解-三连击p.s


    Telephone Lines 二分答案+最短路

    Telephone Lines

    Solution:

    钱的花费显然具有单调性,即花更多的钱得到的方案中,一定包含花费更少的方案。

    所以可以二分答案,转化为判定性问题。

    然后我们发现很简单了,只需每次check边权大于当前二分的钱的边的数量是不是小于等于K即可。

    Code ↓ :

    IL bool spfa() {
        RG int i,x,y;
        memset(inq,0,sizeof(inq));
        memset(dis,0x3f,sizeof(dis));
        q.push(1),dis[1]=0,inq[1]=1;
        while (!q.empty()) {
            x=q.front(),q.pop(),inq[x]=0;
            for (i=head[x];i;i=e[i].next) 
                if (dis[y=e[i].to]>dis[x]+e[i].ver) {
                    dis[y]=dis[x]+e[i].ver;
                    if (!inq[y]) q.push(y),inq[y]=1;
                }
        }
        return dis[n]<=k;
    }
    
    IL bool check() {
        RG int i;
        tot=0;
        memset(&e,0,sizeof(e));
        memset(head,0,sizeof(head));
        for (i=1;i<=m;++i) make(fr[i],to[i],(v[i]>mid));
        return spfa();
    }
    
    

    Roads and Planes Topo序+dijkstra

    Roads and Planes

    Solution:

    直接spfa过不了。。。dijkstra又处理不了负边权。。。怎么办⊙_⊙

    注意到所有的无向边不为负,那么我们可以先只考虑连上无向边,得到若干连通块。

    那么,对于每个连通块,显然是能够用dijkstra得到当前连通块内各点的最短路的。

    然后再考虑加上有向边。

    如果把每个连通块看成一个点,那么这张图就是一张DAG

    那么就可以做Topo排序,把连通块之间的最短路关系传递一下就可以了。

    Code↓:

    #include<bits/stdc++.h>
    #define RG register
    #define IL inline
    #define DB double 
    #define LL long long
    #define mp make_pair
    using namespace std;
    
    IL int gi() {
        RG int x=0,w=0; char ch=0;
        while (ch<'0'||ch>'9') {if (ch=='-') w=1;ch=getchar();}
        while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
        return w?-x:x;
    }
    
    const int N=25001;
    const int M=50001;
    const int INF=0x3f3f3f3f;
    
    vector<int> p[N];
    queue<int> q1;
    priority_queue< pair<int,int> > q2;
    int T,R,P,S,num,tot,head[N],bel[N],ind[N],vis[N],dis[N];
    
    struct Edge{int next,to,ver;}e[M<<2];
    IL void make(int a,int b,int c) {e[++tot]=(Edge){head[a],b,c},head[a]=tot;}
    
    void dfs(int x) {
        RG int i,y;
        bel[x]=num,p[num].push_back(x);
        for (i=head[x];i;i=e[i].next)
            if (!bel[y=e[i].to]) dfs(y);
    }
    
    int main ()
    {
        RG int i,hd,x,y,z;
        T=gi(),R=gi(),P=gi(),S=gi();
        for (i=1;i<=R;++i)
            x=gi(),y=gi(),z=gi(),make(x,y,z),make(y,x,z);
        for (i=1;i<=T;++i)
            if (!bel[i]) ++num,dfs(i);	
        for (i=1;i<=P;++i) {
            x=gi(),y=gi(),z=gi(),make(x,y,z);
            if (bel[x]!=bel[y]) ++ind[bel[y]];
        }
        memset(dis,127,sizeof(dis));
        dis[S]=0,q1.push(bel[S]);
        for (i=1;i<=num;++i)
            if (!ind[i]) q1.push(i);
        while (!q1.empty()) {
            hd=q1.front(),q1.pop();
            for (i=0;i<p[hd].size();++i)
                q2.push(mp(-dis[p[hd][i]],p[hd][i]));
            while (!q2.empty()) {
                x=q2.top().second,q2.pop();
                if (vis[x]) continue;
                vis[x]=1;
                for (i=head[x];i;i=e[i].next) {
                    if (dis[y=e[i].to]>dis[x]+e[i].ver) {
                        dis[y]=dis[x]+e[i].ver;
                        if (bel[x]==bel[y]) q2.push(mp(-dis[y],y));
                    }
                    if (bel[x]!=bel[y]&&--ind[bel[y]]==0) q1.push(bel[y]);
                }
            }
        }
        // 整体Topo排序 局部dijkstra
        for (i=1;i<=T;++i)
            if (dis[i]>0x3f3f3f3f) puts("NO PATH");
            else printf("%d
    ",dis[i]);
        return 0;
    }
    

    Cow Relays Floyd+矩阵乘法

    Cow Relays

    Solution:

    先把边给离散化一下。

    不妨设邻接矩阵A[k]代表恰好经过k条边的情况。

    具体而言就是:这个矩阵中一对点i,j,A[k][i][j]表示的是恰好经过K条边的i,j间的最短路。

    考虑怎么转移过来,由Floyd算法可知:

    A[k][i][j]=min{A[x][i][p]+A[y][p][j]};
    其中x+y=k。
    

    诶,好像和矩阵乘法的形式很像啊:

    O[i][j]=sum{R[i][k]×Z[k][j]};
    

    同时注意到,这个矩阵A也满足结合律。

    所以可以相当于只是把矩阵乘法的求和改为取min,乘积改为求和了。

    Code↓:

    #include <bits/stdc++.h>
    #define RG register
    #define IL inline
    #define LL long long 
    using namespace std;
    int gi(){
        char ch=getchar(); int x=0,q=0;
        while(ch<'0'||ch>'9') q=ch=='-'?1:q,ch=getchar();
        while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
        return q?-x:x;
    }
    
    const int M=210;
    const int N=1e6+10;
    const int INF=0x3f3f3f3f;
    
    int T,n,S,E,cnt,mp[1010];
    
    struct Matrix {
        int MT[M][M];
        IL void clear() {
            RG int i,j;
            for (i=1;i<=200;++i)
                for (j=1;j<=200;++j) MT[i][j]=INF;
        }
    }ver,ans;
    
    IL Matrix handle(Matrix a,Matrix b) {
        RG int i,j,k;
        RG Matrix now;
        now.clear();
        for (i=1;i<=cnt;++i)
            for (j=1;j<=cnt;++j)
                for (k=1;k<=cnt;++k)
                    now.MT[i][j]=min(now.MT[i][j],a.MT[i][k]+b.MT[k][j]);
        return now;
    }
    
    IL void quick_pow(int P) {
        for (--P,ans=ver;P;P>>=1,ver=handle(ver,ver))
            if (P&1) ans=handle(ans,ver);
    }
    
    int main(){
        RG int i,len,x,y;
        n=gi(),T=gi(),S=gi(),E=gi();
        ver.clear();
        for (i=1;i<=T;++i) {
            len=gi(),x=gi(),y=gi();
            if (!mp[x]) mp[x]=++cnt;
            if (!mp[y]) mp[y]=++cnt;
            ver.MT[mp[x]][mp[y]]=ver.MT[mp[y]][mp[x]]=len;
        }
        quick_pow(n);
        printf ("%d
    ",ans.MT[mp[S]][mp[E]]);
        return 0;
    }
    
    

    The End

  • 相关阅读:
    js中(function(){…})()立即执行函数写法理解
    JS 立即执行的函数表达式(function)写法
    javascript中call,apply,bind的用法对比分析
    C++成员函数指针的应用
    typeid详解
    dynamic_cast
    C++标准转换运算符dynamic_cast
    继承的构造函数
    考虑写一个不抛出异常的swap函数
    布隆过滤器(转)
  • 原文地址:https://www.cnblogs.com/Bhllx/p/10615796.html
Copyright © 2020-2023  润新知