hdu 3491 Thieves(最小割拆点)

题意:有n个城市和m条道路(双向),一伙小偷准备从S城出发到H城盗窃,为了将这伙小偷抓住,需要在这n个城市中的每一个城市安排一定数量的警察(每个城市警察的数量已经给出),但警察不希望在S城或H城遇到小偷.求解总共需要的最少警察数.

由于每个城市顶点都具有权值,所以对于每个城市拆成两个点u和所对应的u',之间连容量为w的边,S,H两点不会算在最小割中,所以将这两点拆点,拆点后容量为无穷,添加源点s和汇点t,加边(s,S,INF)和(H+n,t,INF),对于两相连的城市之间连容量为无穷大的双向边,然后求解最大流即可.

 

#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=400 ;
const int M=300000 ;
const int inf=1<<30 ;
struct node
{
    int u , v, c, next ;
}edge[M];
int  head[N],gap[N],dis[N],cur[N],pre[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 n)
{
    
    int flow=0,aug=inf,u;
    memset(dis,0,sizeof(dis));
    memset(gap,0,sizeof(gap));
    for(int i=0; i<=n; i++)          cur[i]=head[i];         
    gap[s]=n;
    u=pre[s]=s;
    while(dis[s]<n)
    {
        
        loop :
        for(int &j=cur[u]; j!=-1; j=edge[j].next)
        {
            int v=edge[j].v;
            if(edge[j].c>0&&dis[u]==dis[v]+1)
            {
                
                if(edge[j].c<aug) aug=edge[j].c;
                pre[v]=u;
                u=v;
                if(v==t)     
                {
                    while(u!=s)
                    {
                       u=pre[u];
                       edge[cur[u]].c-=aug;
                       edge[cur[u]^1].c+=aug;
                   }
                    flow+=aug;
                    aug=inf;
                }              
               goto loop ;
            }
        }
        int mindis=n;
        for(int j=head[u]; j!=-1; j=edge[j].next)
        {
            int v=edge[j].v;
            if(edge[j].c>0&&dis[v]<mindis)
            {
                mindis=dis[v];
                cur[u]=j;
            }
        }
        if((--gap[dis[u]])==0)
            break;
        gap[dis[u]=mindis+1]++;
        u=pre[u];
    }
    return flow;
}


int main()
{
    int T,n,m,S,H,x,a,b;
    scanf("%d",&T);
    while(T--)
    {
           scanf("%d%d%d%d",&n,&m,&S,&H); 
           top=0;
           memset(head,-1,sizeof(head));
           int s=0,t=2*n+1;; 
           for(int i = 1 ; i <= n ; i++)
           {
                   scanf("%d",&x);
                     add(i,i+n,x);                    
           }
           for(int i = 1 ; i <= m ; i++)
           {
                    scanf("%d%d",&a,&b);
                    add(a+n,b,inf);           //双向边 
                    add(b+n,a,inf);
           }
           add(s,S,inf);                     //源点到起点 
           add(H+n,t,inf);                    //终点到汇点 
           add(S,S+n,inf);           //S,H两点容量改为inf,因为这两点说不能割; 
           add(H,H+n,inf);
           int ans=sap(s,t,t+1);
           printf("%d\n",ans);
    } 
     return 0;
}


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值