BZOJ 3144: [Hnoi2013]切糕 [最小割]

Hnoi2013切糕问题解析
本文解析了Hnoi2013竞赛中的“切糕”问题,通过将每个点拆分为r个点的方式,利用图论中的最大流算法求解最优解。文中详细展示了具体的代码实现过程。

3144: [Hnoi2013]切糕

题意:略


省选之前口胡过~

每个点拆成r个点

题目要求
\[ \mid f(x,y) - f(x',y') \mid \le D \]
我们可以对每个点都考虑上界,这样其实也把下界考虑了

\((x,y,k)\)\((x,y,k+1)\)\(val(x,y,k)\)的边,向\((x,y,k-D)\)连inf

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
const int N=7e4+5, M=1e6+5, INF=1e9;
inline int read(){
    char c=getchar();int x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}

int n, m, r, d, val, nm, s, t;
inline int id(int i, int j, int k) {return (i-1)*m+j + (k-1)*nm;}

namespace mf {
    struct edge{int v, ne, c, f;} e[M];
    int cnt=1, h[N];
    inline void ins(int u, int v, int c) {
        e[++cnt]=(edge){v, h[u], c, 0}; h[u]=cnt;
        e[++cnt]=(edge){u, h[v], 0, 0}; h[v]=cnt;
    }
    
    int q[N], head, tail, d[N], vis[N];
    bool bfs() {
        memset(vis, 0, sizeof(vis));
        head=tail=1;
        q[tail++]=s; vis[s]=1; d[s]=0;
        while(head != tail) {
            int u = q[head++];
            for(int i=h[u];i;i=e[i].ne) 
                if(!vis[e[i].v] && e[i].c > e[i].f) {
                    vis[e[i].v] = 1; d[e[i].v] = d[u]+1;
                    q[tail++] = e[i].v;
                    if(e[i].v == t) return true;
                }
        }
        return false;
    }
    int cur[N];
    int dfs(int u, int a) {
        if(u==t || a==0) return a;
        int flow=0, f;
        for(int &i=cur[u];i;i=e[i].ne) 
            if(d[e[i].v] == d[u]+1 && (f = dfs(e[i].v, min(a, e[i].c-e[i].f)) ) > 0) {
                flow += f;
                e[i].f += f;
                e[i^1].f -= f;
                a -= f;
                if(a == 0) break;
            }
        if(a) d[u] = -1;
        return flow;
    }
    int dinic() {
        int flow=0;
        while(bfs()) { 
            for(int i=s; i<=t; i++) cur[i] = h[i];
            flow += dfs(s, INF); 
        }
        return flow;
    }
} using mf::ins;

int main() {
    freopen("in", "r", stdin);
    n=read(); m=read(); r=read(); d=read(); nm = n*m;
    s=0; t=n*m*r+1;
    for(int k=1; k<=r; k++) 
        for(int i=1; i<=n; i++) for(int j=1; j<=m; j++) {
            val=read(); int now = id(i, j, k);
            if(k != r) ins(now, now+nm, val);
            else ins(now, t, val);
            if(k-d >= 1) {
                if(i != 1) ins(now, id(i-1, j, k-d), INF);
                if(i != n) ins(now, id(i+1, j, k-d), INF);
                if(j != 1) ins(now, id(i, j-1, k-d), INF);
                if(j != m) ins(now, id(i, j+1, k-d), INF);
            }
        }
    for(int i=1; i<=n; i++) for(int j=1; j<=m; j++) ins(s, id(i, j, 1), INF);
    int ans = mf::dinic();
    printf("%d", ans);
}

转载于:https://www.cnblogs.com/candy99/p/6704566.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值