• 2019 Multi-University Training Contest 1 E Path(最短路+最小割)


    题意

    链接:https://vjudge.net/problem/HDU-6582

    给定一个有向图,可以有重边,每条边上有一个权值表示删掉这条边的代价,问最少花费多少代价能使从s到t节点的最短路径增大?1≤n,m≤10000

    思路

    容易想到应该是删最短路上的边,最短路可能不止一条,所以使原图1到n的所有最短路不连通即可,这就是最小割呀!选出权值和最小的边使得图不连通,这里是使最短路图不连通。

    所以做法就是先建两个图,一个是u->v的有向边,另一个是v->u的有向边,从1跑一下Dijkstra,从n跑一下Dijkstra,我们枚举每条边(u,v),如果这条边是最短路上的边,那么应满足dis1[u]+val(u,v)+dis2[v]=dis1[n],即1到u的最短距离+u到v的距离+v到n的最短距离=1到n的最短距离。

    最小割=最大流,跑一下Dinic即可啦。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define int ll
    const int N=1e6+5;
    const ll inf=1e16;
    
    struct node
    {
        int p,w;
        node(int a,int b)
        {
            p=a;
            w=b;
        }
        friend bool operator<(node a,node b) //权值小的先出队
        {
            if(a.w!=b.w) return a.w>b.w;
            return a.p>b.p;
        }
    };
    vector <node> eg1[N],eg2[N];
    int dis1[N],n,dis2[N];
    void add1(int u,int v,int w)
    {
        eg1[u].push_back(node(v,w));
    }
    void add2(int u,int v,int w)
    {
        eg2[u].push_back(node(v,w));
    }
    void Dijkstra1(int now)
    {
        for(int i=0; i<=n; i++) dis1[i]=inf;
        dis1[now]=0;
        priority_queue <node> pq;
        pq.push(node(now,dis1[now]));
        while(!pq.empty())
        {
            node f=pq.top();
            pq.pop();
            for(int i=0; i<eg1[f.p].size(); i++)
            {
                node t=eg1[f.p][i];
                if(dis1[t.p]>t.w+f.w)
                {
                    dis1[t.p]=t.w+f.w;
                    pq.push(node(t.p,dis1[t.p]));
                }
            }
        }
    }
    void Dijkstra2(int now)
    {
        for(int i=0; i<=n; i++) dis2[i]=inf;
        dis2[now]=0;
        priority_queue <node> pq;
        pq.push(node(now,dis2[now]));
        while(!pq.empty())
        {
            node f=pq.top();
            pq.pop();
            for(int i=0; i<eg2[f.p].size(); i++)
            {
                node t=eg2[f.p][i];
                if(dis2[t.p]>t.w+f.w)
                {
                    dis2[t.p]=t.w+f.w;
                    pq.push(node(t.p,dis2[t.p]));
                }
            }
        }
    }
    /***************************************/
    int x,y,z,maxflow,deep[N];//deep深度
    struct Edge
    {
        int next,to,dis;
    } edge[N];
    int num_edge=-1,head[N],cur[N];//cur用于复制head
    queue <int> q;
    
    void add_edge(int from,int to,int dis,bool flag)
    {
        edge[++num_edge].next=head[from];
        edge[num_edge].to=to;
        if (flag) edge[num_edge].dis=dis;//反图的边权为 0
        head[from]=num_edge;
    }
    
    //bfs用来分层
    bool bfs(int s,int t)
    {
        memset(deep,0x7f,sizeof(deep));
        while (!q.empty()) q.pop();
        for (int i=1; i<=n; i++) cur[i]=head[i];
        deep[s]=0;
        q.push(s);
    
        while (!q.empty())
        {
            int now=q.front();
            q.pop();
            for (int i=head[now]; i!=-1; i=edge[i].next)
            {
                if (deep[edge[i].to]>inf && edge[i].dis)//dis在此处用来做标记 是正图还是返图
                {
                    deep[edge[i].to]=deep[now]+1;
                    q.push(edge[i].to);
                }
            }
        }
        if (deep[t]<inf) return true;
        else return false;
    }
    
    //dfs找增加的流的量
    int dfs(int now,int t,int limit)//limit为源点到这个点的路径上的最小边权
    {
        if (!limit || now==t) return limit;
    
        int flow=0,f;
        for (int i=cur[now]; i!=-1; i=edge[i].next)
        {
            cur[now]=i;
            if (deep[edge[i].to]==deep[now]+1 && (f=dfs(edge[i].to,t,min(limit,edge[i].dis))))
            {
                flow+=f;
                limit-=f;
                edge[i].dis-=f;
                edge[i^1].dis+=f;
                if (!limit) break;
            }
        }
        return flow;
    }
    
    void Dinic(int s,int t)
    {
        while (bfs(s,t))
            maxflow+=dfs(s,t,inf);
    }
    /***********************************************/
    signed main()
    {
        int u,v,m,w,t;
        scanf("%lld",&t);
        while(t--)
        {
    
            scanf("%lld%lld",&n,&m);
            for(int i=0; i<=n; i++) eg1[i].clear(),eg2[i].clear();
            for(int i=0; i<m; i++)
            {
                scanf("%lld%lld%lld",&u,&v,&w);
                add1(u,v,w);
                add2(v,u,w);
            }
            Dijkstra1(1);
            Dijkstra2(n);
            memset(head,-1,sizeof(head));
            for(int i=1; i<=n; i++)
            {
                int sz=eg1[i].size();
                for(int j=0; j<sz; j++)
                {
                    if(dis1[i]+eg1[i][j].w+dis2[eg1[i][j].p]==dis1[n])
                    {
                        add_edge(i,eg1[i][j].p,eg1[i][j].w,1);
                        add_edge(eg1[i][j].p,i,eg1[i][j].w,0);
                    }
                }
            }
            maxflow=0;
            Dinic(1,n);
            printf("%lld
    ",maxflow);
        }
        return 0;
    }
    
  • 相关阅读:
    帮忙看看怎么优化这个最长的sql
    12种不宜使用的Javascript语法
    走格子
    乘法逆元
    完美字符串
    全排列问题
    A. Sorting Railway Cars
    Prim
    矩阵取数
    套题T8&T9
  • 原文地址:https://www.cnblogs.com/mcq1999/p/11428528.html
Copyright © 2020-2023  润新知