最短路习题题解-三连击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\n",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\n",ans.MT[mp[S]][mp[E]]);
    return 0;
}

The End

posted @ 2019-03-28 16:17  薄荷凉了夏  阅读(245)  评论(0编辑  收藏  举报