【BZOJ1266】【AHOI2006】上学路线route 题解

题面:传送门


    这道题要求两个答案,首先,我们先看第一个,求出最短时间,那跑一遍最短路不就行了吗?由于 n 500 ,所以什么方法都能过(除非把floyd写成 n 4 )。

    第二个问题要求删去一些带权无向边,使新图的 1 n 的最短路长度大于原图的 1 n 的最短路长度,并求出删去的最小边权和。看到“最小”两个字,一般不是动态规划,就是网络流,这道题动态规划显然不可行,那我们往网络流上考虑。由于只有删去最短路上的边才能使最短路长度变大。我们可以先求出哪些边在最短路上。那怎么球呢?令 d i s a , b a b 的最短路长度, d a , b a b 的边的长度,那么对于边 ( x , y , d ) ,如果有

d i s 1 , x + d x , y + d i s y , n = d i s 1 , n

    那么这条边一定在一条最短路里。我们将所有的这种边求出来,就得到了一个图,这时我们以删去的代价为边的流量,跑一遍最小割不就行了吗?这时可能有人会问,如果对于一条无向边,它的正反两条边都在最短路图里,并且全删了,代价会被多算一遍吗?其实仔细分析一下会发现,一条边是不可能正反两次都出现在最短路里的,假设对于无向边 a b a b 出现在了最短路中, b a 也出现在了最短路中,则

d i s 1 , a + d a , b = d i s 1 , b
d i s 1 , b + d a , b = d i s 1 , a

    那么

d i s 1 , b = d i s 1 , b + 2 · d a , b

    又

d a , b > 0

    所以不符事实,不存在均出现在最短路中的情况。

    因为有网络流,所以时间复杂度就不在此赘述了。。。

    各位觉得我的分析清楚吗?如果觉得好的话,给个好评撒~~~

    再给个代码理解下:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
int n,m;
int p[130000],q[130000],t[130000],c[130000];
int ds[510],dt[510];
vector<pair<int,int> >g[510];
struct edge{
    int to,cap,rev;
};
vector<edge>G[510];
void addedge(int frm,int to,int cap)
{
    G[frm].push_back((edge){to,cap,G[to].size()});
    G[to].push_back((edge){frm,0,G[frm].size()-1});
}
int lev[510],iter[510];
void bfs(int s,int t)
{
    memset(lev,-1,sizeof(lev));
    lev[s]=0;
    int q[510];
    int head=0,tail=1;
    q[1]=s;
    while(head<tail)
    {
        int x=q[++head];
        if(x==t)return;
        for(int i=0;i<G[x].size();i++)
        {
            edge &e=G[x][i];
            if(e.cap && lev[e.to]<0)
            {
                lev[e.to]=lev[x]+1;
                q[++tail]=e.to;
            }
        }
    } 
}
int dfs(int s,int t,int f)
{
    if(s==t)return f;
    for(int &i=iter[s];i<G[s].size();i++)
    {
        edge &e=G[s][i];
        if(e.cap && lev[e.to]==lev[s]+1)
        {
            int d=dfs(e.to,t,min(f,e.cap));
            if(d)
            {
                e.cap-=d;
                G[e.to][e.rev].cap+=d;
                return d;
            }
        } 
    }
    return 0;
}
int mxflow(int s,int t)
{
    int res=0;
    for(;;)
    {
        bfs(s,t);
        if(lev[t]<0)return res;
        memset(iter,0,sizeof(iter));
        int f;
        while(f=dfs(s,t,1e9))res+=f;
    }
}
void dijk(int x,int dis[])
{
    priority_queue<pair<int,int> ,vector<pair<int,int> > ,greater<pair<int,int> > >q;
    bool vis[510];
    for(int i=1;i<=n;i++)vis[i]=false;
    for(int i=1;i<=n;i++)dis[i]=1e9;
    dis[x]=0;
    q.push(make_pair(0,x));
    for(int i=1;i<=n;i++)
    {
        while(!q.empty() && (vis[q.top().second] || dis[q.top().second]!=q.top().first))q.pop();
        int x=q.top().second;
        q.pop();
        for(int i=0;i<g[x].size();i++)
        {
            int y=g[x][i].first,w=g[x][i].second;
            if(vis[y])continue;
            if(dis[x]+w<dis[y])
            {
                dis[y]=dis[x]+w;
                q.push(make_pair(dis[y],y));
            }
        }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d%d",p+i,q+i,t+i,c+i);
        g[p[i]].push_back(make_pair(q[i],t[i]));
        g[q[i]].push_back(make_pair(p[i],t[i]));
    }
    dijk(1,ds);
    dijk(n,dt);
    for(int i=1;i<=m;i++)
    {
        if(ds[p[i]]+t[i]+dt[q[i]]==ds[n])addedge(p[i],q[i],c[i]);
        if(ds[q[i]]+t[i]+dt[p[i]]==ds[n])addedge(q[i],p[i],c[i]); 
    }
    printf("%d\n%d",ds[n],mxflow(1,n));
    return 0;
} 

猜你喜欢

转载自blog.csdn.net/qq_42112677/article/details/80388031