bzoj5403 marshland 最大费用可行流

18 篇文章 0 订阅

Description


这里写图片描述
这里写图片描述

Solution


唯一会写的题,还写挂了gg( ╯□╰ )

注意到我们放置一个石头等同于选择这个石头相邻的两个不危险的位置,且每个位置只能选一次
考虑费用流。与最小割类似,我们用危险度之和减去最大费用即为最小的答案
我们把每个点拆点连容量为1费用为0来满足不能重复选的限制,危险点的费用设为危险度
对于一个危险点往两个方向的非危险点连边。注意到有些非危险点之间不可能连边,那么我们把点分为三列:奇数行的非危险点,危险点,偶数行的非危险点

不难发现每次最多增广1的流量,当流量满m的时候就能退了。这里求得的实际上是最大费用可行流
大样例是假的,打挂了都能过orz

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <queue>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))

typedef long long LL;
const LL INF=10000000000;
const int N=40005;
const int E=800005;

struct edge {int x,y,w; LL c; int next;} e[E];

int v[55][55],id[55][55],pre[N];
int ls[N],edCnt=1;

LL dis[N];

bool vis[N],rec[N];

int read() {
    int x=0,v=1; char ch=getchar();
    for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
    for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
    return x*v;
}

void add_edge(int x,int y,int w,int c) {
    e[++edCnt]=(edge) {x,y,w,c,ls[x]}; ls[x]=edCnt;
    e[++edCnt]=(edge) {y,x,0,-c,ls[y]}; ls[y]=edCnt;
    // printf("%d %d (%d,%d)\n", x,y,w,c);
}

bool spfa(int st,int ed) {
    std:: queue <int> que;
    que.push(st);
    rep(i,0,ed+2) dis[i]=-INF;
    dis[st]=0; vis[st]=1;
    for (;!que.empty();) {
        int now=que.front(); que.pop();
        for (int i=ls[now];i;i=e[i].next) {
            if (e[i].w>0&&dis[now]+e[i].c>dis[e[i].y]) {
                dis[e[i].y]=dis[now]+e[i].c;
                pre[e[i].y]=i;
                if (!vis[e[i].y]) {
                    vis[e[i].y]=1;
                    que.push(e[i].y);
                }
            }
        } vis[now]=0;
    }
    return dis[ed]!=-INF;
}

LL mcf(int st,int ed,int f) {
    LL ret=0,tot=0;
    for (;f;) {
        if (!spfa(st,ed)) break;
        int mn=f;
        for (int i=ed;pre[i];i=e[pre[i]].x) mn=std:: min(e[pre[i]].w,mn);
        for (int i=ed;pre[i];i=e[pre[i]].x) {e[pre[i]].w-=mn; e[pre[i]^1].w+=mn; tot+=e[pre[i]].c*mn;}
        f-=mn; ret=std:: max(ret,tot);
    }
    return ret;
}

int main(void) {
    freopen("data.in","r",stdin);
    // freopen("marshland.out","w",stdout);
    int n=read(),m=read(),k=read(),tot=0;
    rep(i,1,n) rep(j,1,n) id[i][j]=++tot;
    LL sum=0;
    rep(i,1,n) rep(j,1,n) {
        v[i][j]=read();
        sum+=v[i][j];
    } 
    rep(i,1,k) {
        int x=read(),y=read();
        rec[id[x][y]]=1;
    }
    rep(i,1,n) rep(j,1,n) if (!rec[id[i][j]]) {
        add_edge(id[i][j],id[i][j]+tot,1,v[i][j]);
        if (!((i+j)&1)) {
            if (i&1) add_edge(id[i][j]+tot,tot*2+1,1,0);
            else add_edge(0,id[i][j],1,0);
        }
    }
    rep(i,1,n) if (!(i&1)) {
        rep(j,1,n) {
            if (!(j&1)) {
                add_edge(id[i][j]+tot,id[i-1][j],1,0);
                add_edge(id[i-1][j]+tot,id[i-1][j-1],1,0);
                if (j!=n) add_edge(id[i-1][j]+tot,id[i-1][j+1],1,0);

                add_edge(id[i][j]+tot,id[i][j-1],1,0);
                add_edge(id[i][j-1]+tot,id[i-1][j-1],1,0);
                if (i!=n) add_edge(id[i][j-1]+tot,id[i+1][j-1],1,0);

                if (j!=n) add_edge(id[i][j]+tot,id[i][j+1],1,0);
                if (j!=n) add_edge(id[i][j+1]+tot,id[i-1][j+1],1,0);
                if (i!=n&&j!=n) add_edge(id[i][j+1]+tot,id[i+1][j+1],1,0);

                if (i!=n) add_edge(id[i][j]+tot,id[i+1][j],1,0);
                if (i!=n&&j!=n) add_edge(id[i+1][j]+tot,id[i+1][j+1],1,0);
                if (i!=n) add_edge(id[i+1][j]+tot,id[i+1][j-1],1,0);
            }
        }
    }
    // add_edge(tot*2+2,0,m,0);
    sum=sum-mcf(0,tot*2+1,m);
    printf("%lld\n", sum);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值