[BJOI2006]狼抓兔子

本文解析了一道经典的网络流问题——BZOJ1001及洛谷P4001,该题要求在给定的网格图中找到使左上角到右下角不可达的最小割。文章详细介绍了如何将问题转化为最大流问题,并通过Dinic算法进行求解。同时,还提供了一个关键的优化技巧,即在搜索增广路径过程中,忽略已无贡献的边。

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

题目:BZOJ1001、洛谷P4001。

题目大意:在一张n×m的网格图中,每个格子都与其右、下、右下方各连有一条带权无向边。现在要你割去一些边,使得左上角的点无法到达右下角的点。并且要割掉的边的总权值最小。问最小是多少。

解题思路:题意是求最小割,根据最小割等于最大流的定理,转化为最大流即可。

由于无向图,建反向边时容量和正向边一样。

但本题裸的dinic是过不了的,需要加一个优化:如果沿一条边走,发现返回值为0,则说明这条边已经无任何贡献了,在本次增广中不需要再考虑这条边(代码第54行)。

然后优化一下常数就可以卡过去了。

C++ Code:

#include<stdio.h>
#include<cctype>
#include<cstring>
#define N 1000002
#define INF 0x3f3f3f3f
int n,m,level[N],iter[N],head[N],cnt=0;
struct edge{
    int to,cap,rev,nxt;
}e[N*7];
int q[5000005];
inline int min(int a,int b){return a<b?a:b;}
inline int readint(){
	char c=getchar();
    int p=0;
    for(;!isdigit(c);c=getchar());
    for(;isdigit(c);c=getchar())
    p=p*10+c-'0';
    return p;
}
inline void addedge(int from,int to,int cap){
	++cnt;
	e[cnt]=(edge){to,cap,cnt+1,head[from]};
	head[from]=cnt;
	++cnt;
	e[cnt]=(edge){from,cap,cnt-1,head[to]};
	head[to]=cnt;
}
void bfs(int s){
    memset(level,-1,sizeof(level));
    level[s]=0;
    int l=0,r=1;
    q[1]=s;
    while(l!=r){
        int u=q[l=l%5000000+1];
        for(int i=head[u];i;i=e[i].nxt){
            int v=e[i].to;
            if(level[v]<0&&e[i].cap>0){
                level[v]=level[u]+1;
                q[r=r%5000000+1]=v;
            }
        }
    }
}
int dfs(int u,int t,int f){
    if(u==t)return f;
    for(int& i=iter[u];i;i=e[i].nxt){
        int v=e[i].to;
        if(e[i].cap>0&&level[v]>level[u]){
            int d=dfs(v,t,min(f,e[i].cap));
            if(d){
                e[i].cap-=d;
                e[e[i].rev].cap+=d;
                return d;
            }else level[v]=-1;
        }
    }
    return 0;
}
int max_flow(int s,int t){
    int flow=0;
    while(1){
        bfs(s);
        if(level[t]<0)return flow;
        memcpy(iter,head,sizeof(iter));
        int f;
        while(f=dfs(s,t,INF))flow+=f;
    }
}
int main(){
    n=readint(),m=readint();
    for(int i=1;i<=n;++i)
    for(int j=1;j<m;++j){
        int t=readint();
        addedge((i-1)*m+j,(i-1)*m+j+1,t);
    }
    for(int i=1;i<n;++i)
    for(int j=1;j<=m;++j){
        int t=readint();
        addedge((i-1)*m+j,i*m+j,t);
    }
    for(int i=1;i<n;++i)
    for(int j=1;j<m;++j){
        int t=readint();
        addedge((i-1)*m+j,i*m+j+1,t);
    }
    printf("%d\n",max_flow(1,n*m));
    return 0;
}

 

转载于:https://www.cnblogs.com/Mrsrz/p/8067700.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值