BZOJ1001狼抓兔子

[BZOJ1001]BJOI2006狼抓兔子
建图建不出来啊…

目录

网络流做法

显然是一个最小割模型,让后暴力建图暴力跑最大流即可,注意原图是无向图,所以算上反边每条边实际上要连4次!(代码写的不丑的话是可以卡过的)

最小割转最短路

  • 观察到这题点数边数明显不是跑网络流的数据,我们需要转化一下!
  • 对偶图应用具体请参考周冬2008年集训队论文,这里只简单讲一下对于该题的应用。
  • 我们从最小割做法考虑,所谓最小割就是将原图割断某些边是点分属s所在集合或t所在集合这两个连通块,其中代价就是割断的边的边权之和。
  • 所以,我们考虑构建对偶图,即每一个环围成的区域在对偶图中变成了一个点,对偶图中的点与点之间连一条边,割断原图的边,并且对偶图的边边权与原图中被割断的边的边权相等。
  • 对于最外层没有被割断的边,我们连一条s-t的“虚边”,将外面的区域分为两部分,外层的点向s/t连边即可。
  • 这样显然,在对偶图上跑s-t的最短路,就是原图的最小割了!而且时空复杂度都降了不少!
    苏菲手绘对偶图

代码

建图时一定要对对偶图的点有系统性的标号,不然要炸啊…

#include <cstdio>
#include <queue>
#include <utility>
#include <climits>
#include <algorithm>
#include <vector>
#include <functional>

using std::priority_queue;
using std::pair;
using std::min;
using std::swap;
using std::vector;
using std::greater;

const int MAXN=2e6+5,MAXM=6e6+5;

int n,m,s,t,ans;

priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > hp;

struct node{
    int he,dis;
    bool use;
    node(){dis=INT_MAX;}
}d[MAXN];
struct line{int to,nex,w;}ed[MAXM];

inline void addE(int u,int v,int w){
    static int cnt=0;
    ed[++cnt]=(line){v,d[u].he,w};
    d[u].he=cnt;
}

inline void Dijsktra(){
    d[0].dis=0;
    hp.push(pair<int,int>(d[0].dis,0));
    pair<int,int> tmp;
    while(hp.size()){
        tmp=hp.top();
        hp.pop();
        if(d[tmp.second].use)
            continue;
        d[tmp.second].use=true;
        for(int i=d[tmp.second].he,v,w;i;i=ed[i].nex){
            v=ed[i].to,w=ed[i].w;
            if(d[tmp.second].dis+w<d[v].dis){
                d[v].dis=d[tmp.second].dis+w;
                hp.push(pair<int,int>(d[v].dis,v));
            }
        }
    }
}

/*
    |-----|-----|-----|-----|
    | 1\ 2| 3\ 4| 5\ 6| 7\ 8|
    | 9\10|11\12|13\14|15\16|
    |17\18|19\20|21\22|23\24|
    |-----|-----|-----|-----|
*/

inline void build(){
    t=(((n-1)*(m-1))<<1)|1;
    for(int i=1,u,w;i<m;++i){
        scanf("%d",&w);
        u=i<<1;
        addE(u,t,w),addE(t,u,w);
    }
    for(int i=2;i<n;++i){
        for(int j=1,u,v,w;j<m;++j){
            scanf("%d",&w);
            u=(((i-2)*(m-1)+j)<<1)-1;
            v=((i-1)*(m-1)+j)<<1;
            addE(u,v,w),addE(v,u,w);
        }
    }
    for(int i=1,u,w;i<m;++i){
        scanf("%d",&w);
        u=(((n-2)*(m-1)+i)<<1)-1;
        addE(u,s,w),addE(s,u,w);
    }
    for(int i=1;i<n;++i){
        for(int j=1,u,v,w;j<=m;++j){
            scanf("%d",&w);
            if(j==1){
                u=(((i-1)*(m-1)+j)<<1)-1;
                addE(u,s,w),addE(s,u,w);
            }
            else if(j==m){
                u=((i-1)*(m-1)+j-1)<<1;
                addE(u,t,w),addE(t,u,w);
            }
            else{
                u=((i-1)*(m-1)+j-1)<<1;
                v=u+1;
                addE(u,v,w),addE(v,u,w);
            }
        }
    }
    for(int i=1;i<n;++i){
        for(int j=1,u,v,w;j<m;++j){
            scanf("%d",&w);
            u=(((i-1)*(m-1)+j)<<1)-1;
            v=u+1;
            addE(u,v,w),addE(v,u,w);
        }
    }
}

int main(){
    scanf("%d%d",&n,&m);
    if(n==1 || m==1){
        ans=INT_MAX;
        if(n>m) swap(n,m);
        for(int i=1,w;i<=m;++i){
            scanf("%d",&w);
            ans=min(ans,w);
        }
        printf("%d",ans);
    }
    else{
        build();
        Dijsktra();
        printf("%d",d[t].dis);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值