原题链接:http://soj.sysu.edu.cn/1402
思路:
最大流经典问题。(1)设置源点(标号为0的房间设置为源点,其他房间n的标号设置为n+1),如果房间k有入侵者(intruder),则存在一条有源点指向k的弧,且容量为c(0,k)=INF;c(k,0)=INF;(2)处理由门联通的两个房间的关系了。如果房间i和j有门且cp在i,则c(i,j)=INF;c(j,i)=1;(3)汇点t就是要保护的房间号码。计算从0到t的最大流ans。若ans>=INF,说明无法保护,否则ans为需要锁上的门的最小数量(即最大流)。
代码:
#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
#include <cstring>
using namespace std;
#define N 25
//顶点数
#define INF 400
//边的最大容量
struct Node{
int to;//终点
int cap; //容量
int rev; //反向边的位置
};
vector<Node> v[N];
void add_Node(int from,int to,int cap) {
int rev1,rev2;
rev1=v[to].size();
v[from].push_back((Node){to,cap,rev1});
rev2=v[from].size()-1;
v[to].push_back((Node){from,0,rev2});
}
int dfs(int s,int t,int f,bool used[]){
if(s==t)
return f;
used[s]=true;
for(int i=0;i<v[s].size();i++){
Node &tmp = v[s][i];
if(used[tmp.to]==false && tmp.cap>0){
int d=dfs(tmp.to,t,min(f,tmp.cap),used);
if(d>0){
tmp.cap-=d;
v[tmp.to][tmp.rev].cap+=d;
return d;
}//更新残余网络
}
}
return 0;
}
int max_flow(int s,int t){
int flow=0;
while(1){
bool used[N];
memset(used,false,sizeof(used));
int f=dfs(s,t,INF,used);
if(f==0)
return flow;
flow+=f;
}
}
int main(){
int testcase;
cin>>testcase;
while(testcase--){
//更新
for(int i=0;i<N;i++)
v[i].clear();
int m,end;
//end为被保护者的位置
vector<int>start;
start.clear();
cin>>m>>end;
for(int i=0;i<m;i++){
string s;
cin>>s;
if(s=="I")
start.push_back(i);
int num;
cin>>num;
int to;
for(int j=0;j<num;j++){
cin>>to;
add_Node(i+1,to+1,INF);
add_Node(to+1,i+1,1);
}
}
//把0置为起点,指向有侵入者的房间
//其他点的编号都要+1
for(int i=0;i<start.size();i++){
add_Node(0,start[i]+1,INF);
add_Node(start[i]+1,0,INF);
}
int ans;
ans=max_flow(0,end+1);
if(ans>=INF)
cout<<"PANIC ROOM BREACH"<<endl;
else
cout<<ans<<endl;
}
return 0;
}