BZOJ1001 狼抓兔子 详解 (平面图 spfa)

题目大意:

求网格图的最小割。


题解:

把平面图中的每一个面积块转化为一个结点,并在网格外的面积块中分出两个面积块分别作为S和T,spfa求出的最短路即为最小割。

因为只要选择了一条联通的S-T路径,图一定被割为两部分。


update!

把图建好之后应该是这样的
把图建好之后应该是这样的,每条新边(彩色边)的边权就是它割开的原图中那条边的边权。


这里写图片描述


如果选择了一条从S到T的路径(如图中浅绿色路径所示),那么就相当于选择了一些原图中的边并将其割掉(图中深绿色的边)。但是图中的路径并不一定是最短的。

已知这样的一个割一定能将start和end分开,所以只要选择一个最小的这样的割就可以了,也就是说选择一条最短的S-T路径就是它的最小割。


代码:

#include <cstdio>
#include <iostream>
#include <queue>
using namespace std;

const int maxn=int(2e3)+111, maxd=int(2e6)+111, maxe=int(4e6);
int n,m;
int S,T;
#define pos(x,y) (S+((x)-1)*m+(y))
int tot=0,head[maxd];

struct Edge {
    int from,to,val,next;
    Edge() {}
    Edge(int x,int y,int v,int nx):from(x),to(y),val(v),next(nx) {}
}eage[maxe*2];

void add(int x,int y,int val) {
    eage[tot]=Edge(x,y,val,head[x]), head[x]=tot++;
    eage[tot]=Edge(y,x,val,head[y]), head[y]=tot++;
    return;
}

const long long INF=(1ll<<62);
queue<int> que;
long long dis[maxd];
bool used[maxd];

int main() {
#ifndef ONLINE_JUDGE
    freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);
#endif // ONLINE_JUDGE

    scanf("%d%d",&n,&m);
    n=n-1, m=2*(m-1);
    S=1, T=pos(n,m)+1;
    for(int i=S;i<=T;i++) head[i]=-1;

    for(int i=1;i<=n+1;i++)
    for(int j=1;j<=m/2;j++) {
        int val;
        scanf("%d",&val);
        if(i==1) add(pos(1,j*2),T,val);
        else if(i<=n) add(pos(i-1,j*2-1),pos(i,j*2),val);
        else add(S,pos(n,j*2-1),val);
    }
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m/2+1;j++) {
        int val;
        scanf("%d",&val);
        if(j==1) add(S,pos(i,j*2-1),val);
        else if(j<=m/2) add(pos(i,(j-1)*2),pos(i,j*2-1),val);
        else add(pos(i,m),T,val);
    }
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m/2;j++) {
        int val;
        scanf("%d",&val);
        add(pos(i,j*2-1),pos(i,j*2),val);
    }

    for(int i=S;i<=T;i++)
        dis[i]=INF, used[i]=false;

    que.push(S);
    dis[S]=0;
    used[S]=true;

    while(que.size()) {
        int u=que.front(); que.pop();
        used[u]=false;
        for(int i=head[u];~i;i=eage[i].next) if(dis[eage[i].to]>dis[u]+eage[i].val) {
            int v=eage[i].to;
            dis[v]=dis[u]+eage[i].val;
            if(!used[v]) {used[v]=true; que.push(v);}
        }
    }
    printf("%lld\n",dis[T]);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值