poj 3084(最小割)

大意就是,有一些房间,初始时某些房间之间有一些门,并且这些门是打开的,也就是可以来回走动的,但是这些门是确切属于某个房间的,也就是说如果要锁门,则只有在那个房间里才能锁,这跟现实很符合啊,不然随便个人站你家外边把你门给锁了是什么情况。 现在一些房间里有一些恐怖分子,要搞破坏,但是我们现在有个房间很重要,不能被他们破坏,这就需要锁一部分的门,不让恐怖分子有可趁之机,那么最少需要锁多个门呢?

很容易联系到最小割上面,刚开始我没读清楚,觉得既然门是可以来回走的,那么建个双向边好了,结果发现不是这样啊。

假设a->b代表a房间有个门到b,那么显然锁门的控制权在a上,锁1个门就可以不让b里面的人到a去,于是我们就建边b-a,容量为1,然后再建立边a->b,容量为无穷大,

为啥为无穷大呢,因为你无论怎么锁门,你在a里边可以随便开门,随便进入b,所以a->b这条边是锁不掉的

然后建立源点,与所有的存在恐怖分子的房间连边,容量为无穷大,因为题目也说了,可能有很多的锁很多的恐怖分子。

最后以被保护的那个房间为汇点,求最大流即可


#include<cstdio>
#include<cstring>
#include<map>
#include<vector>
#include<cmath>
#include<cstdlib>
#include<stack>
#include<queue>
#include <iomanip>
#include<iostream>
#include<algorithm>
using namespace std ;
const int N=100 ;
const int M=20000 ;
const int  inf = 1<<30 ;
struct node
{
	int u,v,c,next;
}edge[M] ;
int head[N],pre[N],cur[N],dis[N],gap[N] ;
int top ;

void add(int u ,int v,int c)         
{        
    edge[top].u=u;        
    edge[top].v=v;        
    edge[top].c=c;        
    edge[top].next=head[u];        
    head[u]=top++;        
    edge[top].u=v;        
    edge[top].v=u;        
    edge[top].c=0;        
    edge[top].next=head[v];        
    head[v]=top++;        
}    
int sap(int s,int t ,int nv)      
{      
    memset(dis,0,sizeof(dis));      
    memset(gap,0,sizeof(gap));      
    int u,v,minflow=inf,flow=0;      
    for(int i = 0 ; i <nv ; i++)  cur[i]=head[i] ;      
    u=pre[s]=s;      
    gap[s]=nv;      
    while(dis[s] < nv )      
    {      
            loop :      
               for(int &j=cur[u] ; j!=-1 ; j=edge[j].next)                     
               {      
                     v=edge[j].v ;      
                     if(edge[j].c > 0 && dis[u] == dis[v] +1 )      
                     {      
                              minflow = min(minflow ,edge[j].c) ;      
                              pre[v]=u;      
                              u=v ;      
                          if(v==t)      
                          {        
                                for( u =pre[v] ; v!=s ;v=u,u=pre[u])      
                                      {      
                                            edge[cur[u]].c -= minflow ;       
                                            edge[cur[u]^1].c += minflow ;      
                                      }       
                                flow += minflow ;      
                                minflow = inf ;      
                          }      
                         goto loop ;      
                     }      
               }      
           int mindis=nv ;      
           for(int i = head[u] ; i!=-1 ; i=edge[i].next)      
           {      
                  v=edge[i].v ;      
                  if(edge[i].c > 0 && dis[v] < mindis)      
                  {      
                       mindis = dis[v] ;      
                       cur[u]=i ;             
                 }      
           }      
          if(--gap[dis[u]]==0) break ;      
          gap[ dis[u] = mindis +1 ]++  ;      
          u=pre[u] ;      
    }      
    return flow ;      
}    

int main()
{
	int T ,t,q,x,n;
	char str[100] ;
	scanf("%d",&T);
	while(T--) 
	{
		    scanf("%d%d",&n,&t) ;
		    t++;
		    int s = 0 ;
		    top = 0 ;
		    memset(head,-1,sizeof(head)) ;
		    for(int i = 1 ; i <= n ; i++)
		    {
		    	  scanf("%s%d",str,&q) ;
		    	  while(q--)
		    	  {
		    	  	   scanf("%d",&x) ;
		    	  	   add(i,x+1,inf) ;
		    	  	   add(x+1,i,1) ;
		    	  }
				  if(str[0] == 'I') 
				    add(s,i,inf) ;
		    }
			int ans = sap(s,t,n+1) ;
	        if(ans >= inf )  puts("PANIC ROOM BREACH");		
			else  printf("%d\n",ans) ;
	} 
	return 0;
} 


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值