poj 3084 Panic Room 【最小割】

Panic Room
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 1975 Accepted: 1027

Description

You are the lead programmer for the Securitron 9042, the latest and greatest in home security software from Jellern Inc. (Motto: We secure your stuff so YOU can't even get to it). The software is designed to "secure" a room; it does this by determining the minimum number of locks it has to perform to prevent access to a given room from one or more other rooms. Each door connects two rooms and has a single control panel that will unlock it. This control panel is accessible from only one side of the door. So, for example, if the layout of a house looked like this: 
 
with rooms numbered 0-6 and control panels marked with the letters "CP" (each next to the door it can unlock and in the room that it is accessible from), then one could say that the minimum number of locks to perform to secure room 2 from room 1 is two; one has to lock the door between room 2 and room 1 and the door between room 3 and room 1. Note that it is impossible to secure room 2 from room 3, since one would always be able to use the control panel in room 3 that unlocks the door between room 3 and room 2. 

Input

Input to this problem will begin with a line containing a single integer x indicating the number of datasets. Each data set consists of two components:
  1. Start line – a single line "m n" (1 <=m<= 20; 0 <=n<= 19) where m indicates the number of rooms in the house and n indicates the room to secure (the panic room).
  2. Room list – a series of m lines. Each line lists, for a single room, whether there is an intruder in that room ("I" for intruder, "NI" for no intruder), a count of doors c (0 <= c <= 20) that lead to other rooms and have a control panel in this room, and a list of rooms that those doors lead to. For example, if room 3 had no intruder, and doors to rooms 1 and 2, and each of those doors' control panels were accessible from room 3 (as is the case in the above layout), the line for room 3 would read "NI 2 1 2". The first line in the list represents room 0. The second line represents room 1, and so on until the last line, which represents room m - 1. On each line, the rooms are always listed in ascending order. It is possible for rooms to be connected by multiple doors and for there to be more than one intruder!

Output

For each dataset, output the fewest number of locks to perform to secure the panic room from all the intruders. If it is impossible to secure the panic room from all the intruders, output "PANIC ROOM BREACH". Assume that all doors start out unlocked and there will not be an intruder in the panic room.

Sample Input

3
7 2
NI 0
I 3 0 4 5
NI 2 1 6
NI 2 1 2
NI 0
NI 0
NI 0
7 2
I 0
NI 3 0 4 5
NI 2 1 6
I 2 1 2
NI 0
NI 0
NI 0
4 3
I 0
NI 1 2
NI 1 0
NI 4 1 1 2 2

Sample Output

2
PANIC ROOM BREACH

1

大致题意:有N个房间(编号从0到N-1)和一些连通这些房间的门,在这N个房间里面有一个特殊的房间M。现在有一些入侵者入侵到一些房间,为了保护房间M不被入侵者入侵,我们需要锁上一些门使得这些入侵者不能到达房间M。

输入有N行分别代表每个房间的信息。

每一行行开头有一个字符串,若为NI表示该房间没有被入侵,反之说明该房间已经被入侵。

字符串后面有一个整数num,后面跟着num个整数,表示房间的编号,代表当前房间到这num个房间有一个门。

特别的,房间a -> b之间有一个门。(不代表b -> a也有门)

若门没有上锁,a可以到达b,b也可以到达a;在门上锁后,a依旧可以到达b,但是b不能到达a。

给你以上信息,让你求出保护房间M不被入侵需要锁上的最少门数,若无法保护输出PANIC ROOM BREACH。

较简单的题目,只说下建图。

建图:设置超级源点source,超级汇点sink(这里sink就是要保护的房间编号)

1,source向所有被入侵的房间建边,容量为无穷大INF(用处在下面);

2,若a -> b有门,则a到b建边容量为无穷大INF(表示锁上门也不会影响a到b,即这条边不能割掉),b到a建边容量为1。

最后跑一次最大流。若达到汇点的流量大于或等于INF,说明有至少一个房间的入侵者到达汇点,这时不能保护房间M。反之则说明source的流量无法到达sink,即入侵者无法入侵房间M,这时的最大流就是保护房间M所需要锁上的最少门数 —— 最小割。

AC代码:

#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#define MAXN 50
#define MAXM 5000
#define INF 0x3f3f3f3f
using namespace std;
struct Edge
{
    int from, to, cap, flow, next;
};
Edge edge[MAXM];
int head[MAXN], edgenum;
int dist[MAXN], cur[MAXN];
bool vis[MAXN];
int source, sink;
int N;
void init()
{
    edgenum = 0;
    memset(head, -1, sizeof(head));
}
void addEdge(int u, int v, int w)
{
    Edge E1 = {u, v, w, 0, head[u]};
    edge[edgenum] = E1;
    head[u] = edgenum++;
    Edge E2 = {v, u, 0, 0, head[v]};
    edge[edgenum] = E2;
    head[v] = edgenum++;
}
void getMap()
{
    source = 0;
    char op[10];
    int num, a;
    for(int i = 1; i <= N; i++)
    {
        scanf("%s%d", op, &num);
        while(num--)
        {
            scanf("%d", &a);
            a++;
            addEdge(i, a, INF);//不能被割断
            addEdge(a, i, 1);
        }
        if(op[0] == 'I')//有入侵者
            addEdge(source, i, INF);
    }
}
bool BFS(int s, int t)
{
    queue<int> Q;
    memset(dist, -1, sizeof(dist));
    memset(vis, false, sizeof(vis));
    dist[s] = 0;
    vis[s] = true;
    Q.push(s);
    while(!Q.empty())
    {
        int u = Q.front();
        Q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            Edge E = edge[i];
            if(!vis[E.to] && E.cap > E.flow)
            {
                dist[E.to] = dist[u] + 1;
                if(E.to == t) return true;
                vis[E.to] = true;
                Q.push(E.to);
            }
        }
    }
    return false;
}
int DFS(int x, int a, int t)
{
    if(x == t || a == 0) return a;
    int flow = 0, f;
    for(int &i = cur[x]; i != -1; i = edge[i].next)
    {
        Edge &E = edge[i];
        if(dist[E.to] == dist[x] + 1 && (f = DFS(E.to, min(a, E.cap-E.flow), t)) > 0)
        {
            edge[i].flow += f;
            edge[i^1].flow -= f;
            flow += f;
            a -= f;
            if(a == 0) break;
        }
    }
    return flow;
}
int Maxflow(int s, int t)
{
    int flow = 0;
    while(BFS(s, t))
    {
        memcpy(cur, head, sizeof(head));
        flow += DFS(s, INF, t);
    }
    return flow;
}
int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%d", &N, &sink);
        sink++;//注意加1
        init();
        getMap();
        int ans = Maxflow(source, sink);
        if(ans >= INF)//流量大于或等于无穷大 说明有入侵者到达汇点
            printf("PANIC ROOM BREACH\n");
        else
            printf("%d\n", ans);
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值