题意:
有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;
}