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

a 夏天 提交于 2019-11-27 02:41:51

题目链接: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\n", 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\n");
            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\n", dinic(1, n)); 
    }
    return 0;
}
优先队列优化dij+最大流dinic

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!