• HDU6582 Path【优先队列优化最短路 + dinic最大流 == 最小割】


    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6582

    来源:2019 Multi-University Training Contest 1

    题目大意:

    给定一张有向图,可以阻碍若干条有向边,花费为边的权值,求使其最短路变得更长所需的最小花费。

    解题思路:

    1.因为最短路可能是多条,所以找出最短路网络,然后在最短路网络中跑最小割,即最大流。就切断了原先的最短路且保证了是最小花费(最小割)。

    2.值得注意的地方:边的长度限制为1e9,所以最短路数组以及一些其他的求和的值要开long long型,防止爆。

    3.求最短路核心边需满足 dis[u] + edge[i].w == dis[v]。那么edge[i].w就是一条最短路的核心边。同时要确保合法性,满足dis[u] != inf && dis[v] != inf

    代码如下:

    #include<stdio.h>
    #include<string.h>
    #include<queue>
    #define LL long long
    #define mem(a, b) memset(a, b, sizeof(a))
    const int MAXN = 1e4 + 100;
    const LL inf = 0x3f3f3f3f;
    using namespace std;
    
    int n, m;//n个点 m条有向边 
    int vis[MAXN];//源点到该点的最短距离是否被确定
    LL dis[MAXN];
    
    struct Edge 
    {
        int to, next;
        LL w;
    }e[MAXN];
    int head[MAXN], cnt;
    
    void add(int a, int b, int c)
    {
        e[++ cnt].to = b;
        e[cnt].w = c;
        e[cnt].next = head[a];
        head[a] = cnt;
    }
    
    struct Node
    {
        int pot;
        LL dis; //点 以及 源点到该点的最短距离
        bool operator < (const Node &a)const//重载 按照dis从小到大排序 
        {
            return dis > a.dis;
        }
    }no;
    
    priority_queue<Node> Q;
    void dij() //优先队列优化的最短路 
    {
        mem(vis, 0), mem(dis, inf);
        no.dis = 0, no.pot = 1;
        dis[1] = 0;
        Q.push(no);
        while(!Q.empty())
        {
            Node a = Q.top();//优先队列无front操作 
            Q.pop();
            if(vis[a.pot])
                continue;
            vis[a.pot] = 1;
            for(int i = head[a.pot]; i != -1; i = e[i].next)//松弛操作 
            {
                int to = e[i].to;
                if(!vis[to] && dis[a.pot] + e[i].w < dis[to])
                {
                    dis[to] = dis[a.pot] + e[i].w;
                    no.pot = to, no.dis = dis[to];
                    Q.push(no);
                }
            }
        }
    //    printf("%d
    ", dis[n]);
    }
    
    struct edge_
    {
        int to, next, flow;
    }e_[2 * MAXN];//要加反向边 开2倍
    int head_[MAXN];
    
    void add_(int a, int b, int c)
    {
        e_[++ cnt].to = b;
        e_[cnt].flow = c;
        e_[cnt].next = head_[a];
        head_[a] = cnt;
    }
    
    int dep[MAXN];
    int bfs(int st, int ed)
    {
        if(st == ed)
            return 0;
        mem(dep, -1);
        queue<int >Q_;
        dep[st] = 1;
        Q_.push(st);
        while(!Q_.empty())
        {
            int now = Q_.front();
            Q_.pop();
            for(int i = head_[now]; i != -1; i = e_[i].next)
            {
                int to = e_[i].to;
                if(e_[i].flow > 0 && dep[to] == -1)
                {
                    dep[to] = dep[now] + 1;
                    Q_.push(to);
                }
            }
        }
        return dep[ed] != -1;
    }
    
    int dfs(int now, int ed, int inc)
    {
        if(now == ed)
            return inc;
        for(int i = head_[now]; i != -1; i = e_[i].next)
        {
            int to = e_[i].to;
            if(e_[i].flow > 0 && dep[to] == dep[now] + 1)
            {
                int min_flow = dfs(to, ed, min(inc, e_[i].flow));
                if(min_flow > 0)
                {
                    e_[i].flow -= min_flow;
                    e_[i ^ 1].flow += min_flow;
                    return min_flow;
                }
            }
        }
        return -1;
    }
    
    LL dinic(int st, int ed)
    {
        LL ans = 0;
        while(bfs(st, ed))
        {
            while(1)
            {
                int inc = dfs(st, ed, inf);
                if(inc == -1)
                    break;
                ans += inc;
            }
        }
        return ans;
    }
    
    int main()
    {
        int T;
        scanf("%d", &T);
        while(T --)
        {
            scanf("%d%d", &n, &m);
            cnt = 0, mem(head, -1); //最短路的边从1开始存 head初始化为-1 
            for(int i = 1; i <= m; i ++)
            {
                int a, b, c;
                scanf("%d%d%d", &a, &b, &c);
                add(a, b, c); //有向边 加一条即可 
            }
            dij(); //跑最短路
            if(dis[n] == inf) //特判 
            {
                printf("0
    ");
                continue;
            }
            cnt = -1, mem(head_, -1);//从0开始存 才能进行 ^ 运算
            for(int i = 1; i <= n; i ++)
            {
                for(int j = head[i]; j != -1; j = e[j].next)
                {
                    int to = e[j].to;
                    if(dis[i] + e[j].w == dis[to] && dis[to] != inf && dis[i] != inf)  //得到最短路核心边(因为可能是多条) 
                    {
                        add_(i, to, e[j].w);
                        add_(to, i, 0); //反向边容量为 0 
                    }
                }
            }
            printf("%lld
    ", dinic(1, n)); 
        }
        return 0;
    }
    优先队列优化dij+最大流dinic
  • 相关阅读:
    第12组 Beta冲刺 (4/5)
    第12组 Beta冲刺 (3/5)
    代码用辅助多了 基础的读取config都忘记了
    wpf 动态添加控件 通过xmal实习 c#代码插入控件
    C#里调用非托管的Dll -z注意 net版本
    动态调用 类库
    c#时间的生成
    c# 第三方 修改版本号 MSBuildTasks, 解决 通配符不匹配问题
    c#.exe以管理员身份运行
    log4
  • 原文地址:https://www.cnblogs.com/yuanweidao/p/11338938.html
Copyright © 2020-2023  润新知