zoj 2788 Panic Room (最小割)

题意:

有n个房间,已知一个要保护的房间编号。每个房间可能向另外的房间有门,但是

门只能从某一面打开。现在某些房间有入侵者,如果要防止入侵者进入要保护的房间

至少要看好多少扇门。


算法:

Dinic求最小割,关键在建图。

由于可能有多个房间有入侵者,所以建一个超级源点。出于习惯,我对应地也建了一个超级汇点。

源点与所有有入侵者进入的房间连INF的边,要保护的房间与汇点之间连INF的边。这样跑最大流时

就能保证这些边不断掉。

如果有一扇门连通A和B,把手在房间A,则从A到B连INF的边,从B到A连边长为1的边。


如果有入侵者的房间和要保护的房间直接相连,且门把手在入侵者的房间则永远无法阻止入侵者进入要

保护的房间。


#include<cstdio>
#include<iostream>
#include<cstring>
#define maxm 410
#define maxn 30
#define INF 0x3f3f3f3f

using namespace std;

struct node
{
    int to,v,next;
}e[maxm<<2];
int head[maxn],cnt,q[maxn],d[maxn];
int st,en,n,flag;

void add(int x,int y,int z)
{
    e[cnt].to = y;
    e[cnt].v = z;
    e[cnt].next = head[x];
    head[x] = cnt++;
    e[cnt].to = x;
    e[cnt].v = 0;
    e[cnt].next = head[y];
    head[y] = cnt++;
}
void init()
{
    memset(head,-1,sizeof(head));
    cnt = 0;
    st = n;
    en = n+1;
    flag = 0;
}

bool bfs()
{
    memset(d,-1,sizeof(d));
    int f = 0,r = 0,u;
    q[r++] = st;
    d[st] = 0;
    while(f<r)
    {
        u = q[f++];
        for(int i=head[u];i!=-1;i=e[i].next)
        {
            int t = e[i].to;
            if(e[i].v>0 && d[t]==-1)//>0
            {
                d[t] = d[u]+1;
                q[r++] = t;
                if(t==en) return true;
            }
        }
    }
    return false;
}

int dfs(int x,int flow)
{
    if(x==en) return flow;
    int ret = 0,dd;
    for(int i=head[x];ret<flow && i!=-1;i=e[i].next)
    {
        int t = e[i].to;
        if(d[t] == d[x]+1 && e[i].v)
        {
            dd = dfs(t,min(flow,e[i].v));
            e[i].v-=dd;
            e[i^1].v+=dd;
            flow-=dd;
            ret+=dd;
        }
    }
    if(!ret) d[x]=-1;
    return ret;
}
int Dinic()
{
    int tmp = 0,maxflow = 0;
    while(bfs())
    {
        while(tmp=dfs(st,INF))
            maxflow+=tmp;
    }
    return maxflow;
}
int main()
{
    int T,x,num,y;
    char sig[5];
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&x);
        init();
        for(int i=0;i<n;i++)
        {
            scanf("%s%d",sig,&num);
            if(i==x)
                add(i,en,INF);
            if(sig[0]=='N')
            {
                if(num==0)
                    continue;
                else
                {
                    for(int j=0;j<num;j++)
                    {
                        scanf("%d",&y);
                        add(y,i,1);
                        add(i,y,INF);
                    }
                }
            }
            else
            {
                add(st,i,INF);
                if(num==0) continue;
                else
                {
                    for(int j=0;j<num;j++)
                    {
                        scanf("%d",&y);
                        if(y==x) flag = 1;
                        add(i,y,INF);
                        add(y,i,1);
                    }
                }
            }
        }
        if(flag) printf("PANIC ROOM BREACH\n");
        else printf("%d\n",Dinic());
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值