[BJOI2006]狼抓兔子

题目: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;
}

 

posted @ 2017-12-19 20:59  Mrsrz  阅读(216)  评论(0编辑  收藏  举报