【BOI2007】逃跑问题 最小割

探讨战犯从监狱逃往村庄的数学问题,利用最小割算法计算在士兵监视下的安全逃脱路径,或需消除的最少士兵数。通过构建网络流模型,连接士兵间的视线覆盖,实现对问题的有效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

  战犯们企图逃离监狱,他们详细地计划了如何逃出监狱本身,逃出监狱之后他们希望在附近的一个村子里找到掩护。村子(下图中的B)和监狱(图中的A)中间有一个峡谷,这个峡谷也是有士兵守卫的。守卫峡谷的士兵们坐在岗哨上很少走动,每个士兵的观察范围是100米。士兵所处位置决定了战犯们能否安全通过峡谷,安全通过的条件就是在任何时刻战犯们距离最近的士兵大于100米。
  给定峡谷的长、宽和每个士兵在峡谷中的坐标,假定士兵的位置一直保持不变,请你写一个程序计算战犯们能否不被士兵发现,顺利通过峡谷。如果不能,那么战犯们最少需要消灭几个士兵才能安全通过峡谷(无论士兵是否被另一个士兵看到,他都可以被消灭)。

题目大意

求最小割

数据范围

1≤W≤50000
1≤L≤50000
1≤N≤250

样例输入

130 340 5
10 50
130 130
70 170
0 180
60 260

样例输出

1

解题思路

最小割,如果两个士兵能互相看见,则前一个向后一个连边。如果一个士兵能看到左边界,则原点向这个士兵连边,如果一个士兵能看见右边界,则这个士兵向汇点连边。然后求最小割。

代码

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cmath>
using namespace std;
inline int Getint(){int x=0,f=1;char ch=getchar();while('0'>ch||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while('0'<=ch&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
struct pos{long long x,y;}w[1000005];
int S,T,N,cnt=0,h[1000005],dis[1000005],GAP[1000005];
struct node{int to,next,v,pair;}e[1000005];
int SAP(int x,int Maxflow){
    if(x==T)return Maxflow;
    int tmp=Maxflow;
    for(int p=h[x];p;p=e[p].next){
        int y=e[p].to;
        int flow=min(tmp,e[p].v);
        if(flow&&dis[x]==dis[y]+1){
            int ret=SAP(y,flow);
            tmp-=ret;
            e[p].v-=ret;
            e[e[p].pair].v+=ret;
            if(!tmp||dis[S]==N)return Maxflow-tmp;
        }
    }
    if(--GAP[dis[x]]==0)dis[S]=N;
    else GAP[++dis[x]]++;
    return Maxflow-tmp;
}
void AddEdge(int x,int y,int v,int pair){e[cnt]=(node){y,h[x],v,pair};h[x]=cnt;}
void AddEdge(int x,int y,int v){AddEdge(x,y,v,++cnt+1);AddEdge(y,x,0,++cnt-1);}
long long Getdis(int a,int b){return (w[a].x-w[b].x)*(w[a].x-w[b].x)+(w[a].y-w[b].y)*(w[a].y-w[b].y);}
int main(){
    int L=Getint(),W=Getint(),n=Getint();
    for(int i=1;i<=n;i++)w[i]=(pos){Getint(),Getint()};
    memset(h,0,sizeof(h));
    memset(e,0,sizeof(e));
    memset(dis,0,sizeof(dis));
    memset(GAP,0,sizeof(GAP));
    N=n*2+2;
    GAP[0]=N;
    cnt=0;
    S=0,T=n*2+1;
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
            if(Getdis(i,j)<=40000LL)
                AddEdge(i+n,j,1<<30),AddEdge(j+n,i,1<<30);
    for(int i=1;i<=n;i++){
        if(w[i].y<=100)AddEdge(S,i,1<<30);
        if(W-w[i].y<=100)AddEdge(i+n,T,1<<30);
    }
    for(int i=1;i<=n;i++)
        AddEdge(i,i+n,1);
    int Ans=0;
    while(dis[S]<N)Ans+=SAP(S,1<<30);
    cout<<Ans;
    return 0;
}

转载于:https://www.cnblogs.com/Cedric341561/p/6811022.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值