多校出了道模板题,于是就开始找模板。这题也是求最小割。
http://poj.org/problem?id=3084
题意:有n个房间,要确保一个房间(t)不能进坏人。相邻的房间可能会有门,但是门上的锁是单向的(只能从一个房间打开进入另一个房间,
a b ,只能从a进入b 不能从b 进入a),初始所有的锁都是开着的,问至少关上几个锁,才能不让坏人进入那个房间。
最小割?此前看到过最大流最小割定理,还以为是最大流的另一种求法呢,原来是用最大流的算法解决最小割问题。。。
构图还是关键:增加一个源点,不能进入坏人的房间(t)是汇点,如果某房间里本来有坏人,就连接这个房间和源点,
权值为inf,如果a->b想通,则a->b的权值为inf ,b->a权值为1(因为锁的控制权在a房间,如果坏人在a房间,这个门是
关不住的(inf),但是坏人如果在b房间,关上这个门只需锁一把锁(1),
还有就是重边问题,因为可能a->b一把锁,b->a一把锁。。。
这个模板是dfs的,估计不是很好。又找了个不是dfs的,还没来得及敲,又去搞代班的事了。
dfs的dinic:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#define inf 9999999
#define Min(a,b) a < b ? a : b
using namespace std;
int head[25],level[25];
int n,t,s_edge;
struct Edge{
int to,w,nxt;
}edge[1102];
void addedge(int u, int v, int w1, int w2){
s_edge++;
edge[s_edge] .to = v;
edge[s_edge].w = w1;
edge[s_edge].nxt = head[u];
head[u] = s_edge;
s_edge++;
edge[s_edge].to = u;
edge[s_edge].w = w2;
edge[s_edge].nxt = head[v];
head[v] = s_edge;
}
int dfs(int x, int cap){ //n inf
if(x == t) return cap;
int tmp = cap;
for(int i = head[x]; i != -1; i = edge[i].nxt){
int to = edge[i].to;
if(level[to] == level[x] + 1 && edge[i].w > 0 && tmp > 0){
int tt = dfs(to,Min(edge[i].w,tmp));
edge[i].w -= tt;
edge[i ^ 1].w += tt;
tmp -= tt;
}
}
return cap - tmp;
}
int bfs(){
int i;
memset(level,-1,sizeof(level));
queue<int > qu;
level[n] = 0;//源点n是第0层
qu.push(n);//让源点进去
while(!qu.empty()){
int tt = qu.front(); qu.pop();
for(i = head[tt]; i != -1; i = edge[i].nxt){
int to = edge[i].to;
if(level[to] == -1 && edge[i].w > 0){
level[to] = level[tt] + 1;
qu.push(to);
}
}
}
if(level[t] != -1) return 1;
return 0;
}
int dinic(){
int sum = 0;
while(bfs()){
sum += dfs(n,inf);//源点 inf 。。。暂时的理解
}
return sum;
}
int main(){
int ca ; char ch[5];
int num,k;
scanf("%d",&ca);
while(ca --){
scanf("%d%d",&n,&t);
s_edge = 1;
memset(head,-1,sizeof(head));
for(int i = 0; i < n; i ++){
scanf("%s",ch);
if(ch[0] == 'I')
addedge(n,i,inf,0);//n为源点 源点到有入侵者的点连个无穷大(锁不住),有入侵者的点到源点连个0(不需要锁)
scanf("%d",&num);
while(num --){
scanf("%d",&k);
addedge(i,k,inf,1);//i 到 k 锁不住, k 到 i 需要一把锁
}
}
int ans = dinic();
if(ans < inf) //???
printf("%d\n",ans);
else
printf("PANIC ROOM BREACH\n");
}
return 0;
}
不是dfs的dinic:
#include<iostream>
#include<cstdio>
#include<string>
using namespace std;
#define inf 1<<30
#define cc(m,v) memset(m,v,sizeof(m))
struct node{
int u,v,f,next;
}edge[1000];
int head[30],p=0,lev[30],cur[30];
int que[1000];
void ainit(){
p=0,cc(head,-1);
}
bool bfs(int s,int t){
int i,u,v,qin=0,qout=0;
cc(lev,0),lev[s]=1,que[qin++]=s;
while(qout!=qin){
u=que[qout++];
for(i=head[u];i!=-1;i=edge[i].next)
if(edge[i].f>0 && lev[v=edge[i].v]==0){
lev[v]=lev[u]+1,que[qin++]=v;
if(v==t) return 1;
}
}
return lev[t];
}
int dinic(int s,int t){
int qin=0,i,k,u,f;
int flow=0;
while(bfs(s,t)){
memcpy(cur,head,sizeof(head));
u=s,qin=0;
while(1){
if(u==t){
for(k=0,f=inf;k<qin;k++)
if(edge[que[k]].f<f)
f=edge[que[i=k]].f;
for(k=0;k<qin;k++)
edge[que[k]].f-=f,edge[que[k]^1].f+=f;
flow+=f,u=edge[que[qin=i]].u;
}
for(i=cur[u];cur[u]!=-1;i=cur[u]=edge[cur[u]].next)
if(edge[i].f>0 && lev[u]+1==lev[edge[i].v]) break;
if(cur[u]!=-1)
que[qin++]=cur[u],u=edge[cur[u]].v;
else{
if(qin==0) break;
lev[u]=-1,u=edge[que[--qin]].u;
}
}
}
return flow;
}
void addedge(int u,int v){
edge[p].u=u,edge[p].v=v,edge[p].f=inf,edge[p].next=head[u],head[u]=p++;
edge[p].u=v,edge[p].v=u,edge[p].f=1,edge[p].next=head[v],head[v]=p++;
}
int main(){
int cas,n,m,u,i,s,t;
bool flag=0;
char as[5];
scanf("%d",&cas);
while(cas--){
ainit();
scanf("%d%d",&n,&t);
for(flag=0,i=0,s=n;i<n;i++){
scanf("%s%d",as,&m);
if(as[0]=='I'){
addedge(s,i);
while(m--){
scanf("%d",&u);
if(u==t) flag=1;
addedge(i,u);
}
}else{
while(m--){
scanf("%d",&u);
addedge(i,u);
}
}
}
if(flag){
cout<<"PANIC ROOM BREACH"<<endl;continue;
}
int flo=dinic(s,t);
if(flo>=inf){
cout<<"PANIC ROOM BREACH"<<endl;continue;
}
printf("%d\n",flo);
}
return 0;
}

本文介绍了一道关于最小割的问题,通过构建特定的图结构,并利用最大流算法求解最小割问题。讨论了如何根据房间之间的单向门设置图的边权,以及使用两种不同的最大流算法实现解决方案。
1914

被折叠的 条评论
为什么被折叠?



