HDU4289 Control (破坏结点使不存在可行流)

题目链接

http://acm.hdu.edu.cn/showproblem.php?pid=4289

读题

给定一个无向图。
一个罪犯要从城市S逃到城市T,为了逮捕罪犯,需要在某些城市设立观察点,罪犯逃到观察点所在的城市就会被逮捕。设立观察点是需要钱的,求最少需花费多少钱在设立观察点上才能万无一失的逮捕罪犯。

解题

罪犯从S逃到T的路线可以看作是一个S到T的可行流。设立观察点相当于破坏结点,故问题变成破坏结点使不存在可行流。有点像最小割模型。但
最小割是破坏边使不存在可行流。针对结点的模型,容易联系到结点容量模型。这里同样可以根据解决结点容量模型的方法——拆点来处理。将结点u拆成u1、u2,u1和u2之间连一条容量为建该观察点费用的有向弧。原指向u的边改成指向u1,原从u指出点边改成从u2指出,容量不变。注意是有向弧,因为虽然一开始给定的是无向图,但是经过拆点后,图就变了一个有向图(不信你画画图)。所以反向边容量是0而不是无向图网路流中的与正向边相等。

这题有两个地方曾WA了,都是由于拆点带来的影响。一个是拆点后汇点不在是T,而是T2(T2的编号与T不一样)。一个是拆点后无向图变成了有向图,反向边的容量为0.

AC代码

//109ms 2732kB
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <map>
#include <vector>
#include <string>
#define INF 0x3f3f3f3f
using namespace std;

int S,T;//源点和汇点
const int maxe=1e5+1000;
const int maxv=1100;
struct edge
{
    int to,w,next;
}e[maxe<<1];
int head[maxv<<1],depth[maxv],cnt;
void init()
{
    memset(head,-1,sizeof(head));
    cnt=-1;
}
void add_edge(int u,int v,int w)
{
    e[++cnt].to=v;
    e[cnt].w=w;
    e[cnt].next=head[u];
    head[u]=cnt;
}
void _add(int u,int v,int w)
{
    add_edge(u,v,w);
    add_edge(v,u,0);
}
bool bfs()
{
    queue<int> q;
    while(!q.empty()) q.pop();
    memset(depth,0,sizeof(depth));
    depth[S]=1;
    q.push(S);

    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=head[u];i!=-1;i=e[i].next)
        {
            if(!depth[e[i].to] && e[i].w>0)
            {
                depth[e[i].to]=depth[u]+1;
                q.push(e[i].to);
            }
        }
    }
    if(!depth[T]) return false;
    return true;
}
int dfs(int u,int flow)
{
    if(u==T) return flow;
    int ret=0;
    for(int i=head[u];i!=-1 && flow;i=e[i].next)
    {
        if(depth[u]+1==depth[e[i].to] && e[i].w!=0)
        {
            int tmp=dfs(e[i].to,min(e[i].w,flow));
            if(tmp>0)
            {
                flow-=tmp;
                ret+=tmp;
                e[i].w-=tmp;
                e[i^1].w+=tmp;
            }
        }
    }
    return ret;
}
int Dinic()
{
    int ans=0;
    while(bfs())
    {
        ans+=dfs(S,INF);
    }
    return ans;
}

int main()
{
    int N,M;
    while(~scanf("%d%d",&N,&M))
    {
        init();
        scanf("%d%d",&S,&T);//汇点拆点后就编号变了
        T+=N;
        for(int i=1;i<=N;i++)
        {
            int x;
            scanf("%d",&x);
            _add(i,i+N,x);
        }
        while(M--)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            _add(u+N,v,INF);//拆点后,无向图变成了有向图
            _add(v+N,u,INF);//故反向边的容量不是与正向边相同,而是0
        }
        printf("%d\n",Dinic());
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_37685156/article/details/80627215
今日推荐