题意和分析转自http://blog.youkuaiyun.com/sdj222555/article/details/7798949
大意就是,有一些房间,初始时某些房间之间有一些门,并且这些门是打开的,也就是可以来回走动的,但是这些门是确切属于某个房间的,也就是说如果要锁门,则只有在那个房间里才能锁,这跟现实很符合啊,不然随便个人站你家外边把你门给锁了是什么情况。 现在一些房间里有一些恐怖分子,要搞破坏,但是我们现在有个房间很重要,不能被他们破坏,这就需要锁一部分的门,不让恐怖分子有可趁之机,那么最少需要锁多个门呢?
很容易联系到最小割上面,刚开始我没读清楚,觉得既然门是可以来回走的,那么建个双向边好了,结果发现不是这样啊。
假设a->b代表a房间有个门到b,那么显然锁门的控制权在a上,锁1个门就可以不让b里面的人到a去,于是我们就建边b-a,容量为1,然后再建立边a->b,容量为无穷大,
为啥为无穷大呢,因为你无论怎么锁门,你在a里边可以随便开门,随便进入b,所以a->b这条边是锁不掉的
然后建立源点,与所有的存在恐怖分子的房间连边,容量为无穷大,因为题目也说了,可能有很多的锁很多的恐怖分子。
最后以被保护的那个房间为汇点,求最大流即可
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define INF 0x1f1f1f
typedef struct {
int v, next, val;
} edge;
const int MAXN = 20010;
const int MAXM = 500010;
edge e[MAXM];
int p[MAXN], eid;
inline void init() {
memset(p, -1, sizeof(p));
eid = 0;
}
//有向
inline void insert1(int from, int to, int val) {
e[eid].v = to;
e[eid].val = val;
e[eid].next = p[from];
p[from] = eid++;
swap(from, to);
e[eid].v = to;
e[eid].val = 0;
e[eid].next = p[from];
p[from] = eid++;
}
//无向
inline void insert2(int from, int to, int val) {
e[eid].v = to;
e[eid].val = val;
e[eid].next = p[from];
p[from] = eid++;
swap(from, to);
e[eid].v = to;
e[eid].val = val;
e[eid].next = p[from];
p[from] = eid++;
}
int n, m; //n为点数 m为边数
int h[MAXN];
int gap[MAXN];
int source, sink;
inline int dfs(int pos, int cost) {
if (pos == sink) {
return cost;
}
int j, minh = n - 1, lv = cost, d;
for (j = p[pos]; j != -1; j = e[j].next){
int v = e[j].v, val = e[j].val;
if(val > 0){
if (h[v] + 1 == h[pos]) {
if (lv < e[j].val) d = lv;
else d = e[j].val;
d = dfs(v, d);
e[j].val -= d;
e[j ^ 1].val += d;
lv -= d;
if (h[source] >= n)return cost - lv;
if (lv == 0)break;
}
if (h[v] < minh)minh = h[v];
}
}
if (lv == cost) {
--gap[h[pos]];
if (gap[h[pos]] == 0)h[source] = n;
h[pos] = minh + 1;
++gap[h[pos]];
}
return cost - lv;
}
int sap(int st, int ed) {
source = st;
sink = ed;
int ret = 0;
memset(gap, 0, sizeof(gap));
memset(h, 0, sizeof(h));
gap[0] = n;
while (h[st] < n)ret += dfs(st, INF);
return ret;
}
int main()
{
int a,i,T,src,end,t;
char s[10];
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
init();
src=n+1;
end=++m;
for(i=1;i<=n;i++){
scanf("%s%d",s,&t);
if(s[0]=='I')insert1(src,i,INF);
while(t--){
scanf("%d",&a);
insert1(i,a+1,INF);
insert1(a+1,i,1);
}
}
n=n+1;
int res=sap(src,end);
if(res>= INF)puts("PANIC ROOM BREACH");
else printf("%d\n", res);
}
return 0;
}