【dinic && 拆点 && 最小割】HDU - 4289 Control

时间:2022-10-03 04:26:29

Problem Description

输入n, m分别代表有n个城市,m条边,输入s,e代表起点,目的点。接下来n行输入w, i行代表城市i的放置检测器所需的价值。接下来m行,输入u,v代表城市u,v之间有边相连接。有*要从s-e问你至少需要多少价值来放检测器。

思路:

建图,然后跑dinic就可以了,核心就是建图。拆点,建边流量为点权。超级源点和起点建边,流量为无穷。目的点和超级汇点建边,流量为无穷。城市与城市之间建边,流量为无穷。核心是城市与城市建边,如何使得到一个城市先经过限流边,在到下一个城市。

#include<bits/stdc++.h>
using namespace std;
struct node
{
    int to, cap, next;
};
#define inf 0x3f3f3f3f
node Map[100000];
int head[999], vis[999], cnt;
void add(int u, int v, int w)//前向星存图
{
    Map[cnt].to = v;
    Map[cnt].cap = w;
    Map[cnt].next = head[u];
    head[u] = cnt++;
    Map[cnt].to = u;
    Map[cnt].cap = 0;
    Map[cnt].next = head[v];
    head[v] = cnt++;
}
bool bfs(int s, int e)
{
    memset(vis, -1, sizeof(vis));
    queue<int> q;
    q.push(s);
    vis[s] = 0;
    while(!q.empty())
    {
        s = q.front(), q.pop();
        for(int i = head[s]; ~i; i = Map[i].next)
        {
            int to = Map[i].to, cap = Map[i].cap;
            if(vis[to] == -1 && cap)
            {
                vis[to] = vis[s] + 1;
                q.push(to);
            }
        }
    }
    if(vis[e] == -1) return 0;
    else return 1;
}
int dfs(int s, int e, int f)
{
    if(s == e) return f;
    int ans = 0;
    for(int i = head[s]; ~i; i = Map[i].next)
    {
        int to = Map[i].to, &cap = Map[i].cap;
        if(vis[to] > vis[s] && cap)
        {
            int d = dfs(to, e, min(f, cap));
            if(d)
            {
                cap -= d;
                Map[i^1].cap += d;
                f -= d;
                ans += d;
                if(!f) break;
            }
        }
    }
    if(ans) return ans;
    vis[s] = -1;
    return 0;
}
int dinic(int s, int e)
{
    int ans = 0;
    while(bfs(s, e))
    {
        ans += dfs(s, e, inf);
    }
    return ans;
}
int main()
{
    int n, m, s, e, w, i, u, v;
    while(~scanf("%d %d", &n, &m))
    {
        cnt = 0;
        memset(head, -1, sizeof(head));
        scanf("%d %d", &s, &e);
        add(0, s, inf);
        add(e+n, 2*n+1, inf);
        for(i = 1; i <= n; i++)
        {
            scanf("%d", &w);
            add(i, i + n, w);
        }
        while(m--)
        {
            scanf("%d %d", &u, &v);//这样建边可以保证,经过点肯定会经过限流的边
            add(u+n, v, inf);
            add(v+n, u, inf);
        }
        printf("%d\n", dinic(0, 2*n+1));//求最大流,就是最小割

    }
}