poj3084 Panic Room 最小割最大流Dinic

题意:有一些建在一起的房间,相互之间通过门相连,一个门的控制开关在它连接的两个房间的其中一个里面,在有开关的房间可以任意进入没开关的另一侧房间,而在另一侧要进入有开关的房间则需要门是开着的。现在有的房间中有入侵者,同时有一个房间n需要保护,问要保护那个房间不被入侵,最少要关上几道门。如果没法实现,则输出“PANIC ROOM BREACH”。比如“I3 0 4 5”,则表示有入侵者,这间房有0,4,5房间的key。

解题:关键部分就是建图。n就是汇点。

题目中要我们保护的房间就是汇点t,然后我们要额外增加一个源点s;
存在intruder的房间,我们将它与源点连一条弧(s,j,INF)。
其他房间如果i->j  则我们要连两条弧 (i,j,INF) (j,i,1); (PS:因为源点直接与存在intruder(入侵者)的房间相连,并且网络是有向图,这个时候问题就被我们转变为,去掉最少权值和的边能使源点与汇点不连通的问题,这就是一个最小割的问题。然后再来就是为什么要连两条弧的问题。因为弧是有向的,如果intruder要从i到j,因为锁在房间i中,所以无论如何都关不住,所以赋值为INF。但是如果intruder要从j进入到i,因为锁在i,所以我们只要关一把锁,就可以了,所以弧赋值为1.)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cmath>
#include<cctype>
using namespace std;
#define MIN(a,b) (a)<(b)? a:b
#define INF 2000000
int cap[25][25],flow[25][25];
int vit[25],lev[25];
int n,m;
bool bfs(int st,int ed,int beg,int End)
{
    memset(lev,0x1f,sizeof(lev));
    int que[25];
    int Front,rear;
    Front=rear=0;
    que[Front++]=beg;
    memset(vit,0,sizeof(vit));
    vit[beg]=1;
    lev[beg]=0;
    while(rear<Front)
    {
        int t=que[rear];
        rear++;
        for(int i=st;i<=ed;i++)
        {
            if(cap[t][i]>flow[t][i]&&!vit[i])
            {
                que[Front++]=i;
                vit[i]=1;
                lev[i]=lev[t]+1;
            }
        }
    }
    if(lev[End]<INF)
        return true;
    return false;

}
int dfs(int v,int st,int ed,int beg,int fl)
{
    int ret=0;
    if(v == n||fl==0)
        return fl;
    for(int i=st;i<=ed;i++)
    {
        if(fl==0)
            break;
        if(cap[v][i]>flow[v][i]&&lev[v]+1==lev[i])
        {
            int f=MIN(cap[v][i]-flow[v][i],fl);
            int r = dfs(i,st,ed,beg,f);
            ret+=r;
            fl-=r;
            flow[v][i]+=r;
            flow[i][v]-=r;
        }
    }
    if(ret==0)
        lev[v] = INF;
    return ret;
}
int dinic(int st,int ed,int beg,int End)
{
    int ret=0;
    while(bfs(st,ed,beg,End))
    {
        int r=dfs(beg,st,ed,beg,INF);
        if(r == 0)
            break;
        ret+=r;
    }
    return ret;
}
int main()
{
    int T,c;
    cin>>T;
    while(T--)
    {
        memset(cap,0,sizeof(cap));
        memset(flow,0,sizeof(flow));
        cin>>m>>n;
        for(int i=0;i<m;i++)
        {
            char ch[5];
            scanf("%s",ch);
            scanf("%d",&c);
            if(strcmp(ch,"I")==0)
                cap[m][i]=INF;
            for(int j=0;j<c;j++)
            {
                int x;
                cin>>x;
                cap[i][x]=INF;
                if(cap[x][i]<INF)
                cap[x][i]++;
            }
        }
        int ret=dinic(0,m,m,n);
        if(ret<INF)
            cout<<ret<<endl;
        else
            cout<<"PANIC ROOM BREACH"<<endl;
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值