「题解」「HNOI2013」切糕

「题解」「HNOI2013」切糕

题目描述

点这里

思路分析及代码

题目分析

这道题的题目可以说得上是史上最难看懂的题目之一了…

首先把题目重新叙述一遍。

题目大致在说,你有一个 P×Q×RP\times Q\times RP×Q×R 的蛋糕,每个点有一个不客观度 v[i][j][k]v[i][j][k]v[i][j][k] ,现在你要把这个蛋糕切开。

切蛋糕的规则是什么呢?

首先我们解释一下:

对于每一竖列,这个竖列的坐标用 (x,y)(x,y)(x,y) 表示。

也就是说,这个竖列上的点的坐标可以表示为 (x,y,i)∣i∈[1,R](x,y,i)|i\in [1,R](x,y,i)i[1,R]

那么,规则很好描述:

我们要在所有的 P×QP\times QP×Q 个竖列中,每个竖列选一个点。

对于一个竖列 (x,y)(x,y)(x,y) 中,把我们选的点的高表示为 f(x,y)f(x,y)f(x,y)

那么很好有 1≤f(x,y)≤R1\le f(x,y)\le R1f(x,y)R

而我们选的点的坐标就是 (x,y,f(x,y))(x,y,f(x,y))(x,y,f(x,y))

竖列相邻:对于一个坐标为 (x,y)(x,y)(x,y) 的竖列,相邻即指坐标为 (i+1,j)、(i−1,j)、(i,j+1)、(i,j−1)(i+1,j)、(i-1,j)、(i,j+1)、(i,j-1)(i+1,j)(i1,j)(i,j+1)(i,j1) 的竖列。

但是有一个限制,对于相邻的竖列,在他们上所选择的点的高度差不超过 DDD ,即:

∣f(x,y)−f(x,y±1)∣≤D , ∣f(x,y)−f(x±1,y)∣≤D|f(x,y)-f(x,y\pm1)|\le D\space, \space |f(x,y)-f(x\pm 1,y)|\le Df(x,y)f(x,y±1)D , f(x,y)f(x±1,y)D

而现在我们的目的是,对于我们所有选出的点,使得 ∑v[i][j][f(i,j)]\sum v[i][j][f(i,j)]v[i][j][f(i,j)] 最小。

题解及代码

这道题怎么思考?

首先,我们考虑:如果没有这个 DDD ,我们应该怎么做?

这个题就转化为:求每一竖列的最短边之和。

这样的问题,似乎几个循环就可以解决。

但是,这样的题是否有些像最小割问题

那么,此题的方法呼之欲出:网络流最大流最小割问题。

如果解决 每一竖列的最短边之和 这样的问题用网络流,建图方法很简单:

建立第 000 层与第 R+1R+1R+1 层,然后有这样的连边关系:

S−>(i,j,1),flow=INFS->(i,j,1),flow=INFS>(i,j,1),flow=INF

(i,j,k)−>(i,j,k+1),flow=v[i][j][k]∣k∈[1,R)(i,j,k)->(i,j,k+1),flow=v[i][j][k]|k\in [1,R)(i,j,k)>(i,j,k+1),flow=v[i][j][k]k[1,R)

(i,j,r)−>T,flow=v[i][j][r](i,j,r)->T,flow=v[i][j][r](i,j,r)>T,flow=v[i][j][r]

但是对于此题,我们还有 DDD 的限制。

现在考虑怎么把这样的限制考虑进我们的网络流。

假设我们有这样一个图:

graph.png

其中, A={p1,p2,p3,p4}A=\{p1,p2,p3,p4\}A={p1,p2,p3,p4} 代表第 aaa 列, B={p5,p6,p7,p8}B=\{p5,p6,p7,p8\}B={p5,p6,p7,p8} 代表第 bbb 列。

断言,现在的 DDD 的值为 111

那么,从题目的表示来说,假设我们在 AAA 中选择了 p3p3p3 ,那么我们就只能在 BBB 中选择 p6,p7,p8p6,p7,p8p6,p7,p8

现在,我们做一个尝试,连接一条边 edge{p3,p6}edge\{p3,p6\}edge{p3,p6}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7JTTWIbK-1576065216359)(https://i.postimg.cc/44c0CL9P/graph-2.png)]

那么,如果我们再跑最大流时,如果我们将 edge{p5,p6}edge\{p5,p6\}edge{p5,p6} 删掉,对 path{S,p1,p2,p3,p6,p7,p8}path\{S,p1,p2,p3,p6,p7,p8\}path{S,p1,p2,p3,p6,p7,p8} 似乎没有影响。

但是又有一个问题:如果我们选的点比 p3p3p3DDD 呢?

其实这个问题是一样的:我们连接 edge{p8,p3}edge\{p8,p3\}edge{p8,p3} ,那么就有这个图:

那么问题就解决了。

所以,我们解决 DDD 对于我们的限制,就是再添加几条边条边:

(i,j,k)−>(i±1,j,k−d)(i,j,k)->(i\pm 1,j,k-d)(i,j,k)>(i±1,j,kd)

(i,j,k)−>(i,j±1,k−d)(i,j,k)->(i,j\pm 1,k-d)(i,j,k)>(i,j±1,kd)

对于 kkk ,满足 k>dk>dk>d

那么,这个题就算是解决了。

接下来是代码。

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

#define rep(i,__l,__r) for(int i=__l,i##_end_=__r;i<=i##_end_;++i)
#define fep(i,__l,__r) for(int i=__l,i##_end_=__r;i>=i##_end_;--i)
#define writc(a,b) fwrit(a),putchar(b)
#define mp(a,b) make_pair(a,b)
#define ft first
#define sd second
#define LL long long
#define ull unsigned long long
#define pii pair<int,int>
#define Endl putchar('\n')
// #define FILEOI
// #define int long long

#ifdef FILEOI
    #define MAXBUFFERSIZE 500000
    inline char fgetc(){
        static char buf[MAXBUFFERSIZE+5],*p1=buf,*p2=buf;
        return p1==p2&&(p2=(p1=buf)+fread(buf,1,MAXBUFFERSIZE,stdin),p1==p2)?EOF:*p1++;
    }
    #undef MAXBUFFERSIZE
    #define cg (c=fgetc())
#else
    #define cg (c=getchar())
#endif
template<class T>inline void qread(T& x){
    char c;bool f=0;
    while(cg<'0'||'9'<c)f|=(c=='-');
    for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    if(f)x=-x;
}
inline int qread(){
    int x=0;char c;bool f=0;
    while(cg<'0'||'9'<c)f|=(c=='-');
    for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    return f?-x:x;
}
template<class T,class... Args>inline void qread(T& x,Args&... args){qread(x),qread(args...);}
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}
inline int gcd(const int a,const int b){return b?gcd(b,a%b):a;}
inline void getInv(int inv[],const int lim,const int MOD){
    inv[0]=inv[1]=1;for(int i=2;i<=lim;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
}
template<class T>void fwrit(const T x){
    if(x<0)return (void)(putchar('-'),fwrit(-x));
    if(x>9)fwrit(x/10);putchar(x%10^48);
}
inline LL mulMod(const LL a,const LL b,const LL mod){//long long multiplie_mod
    return ((a*b-(LL)((long double)a/mod*b+1e-8)*mod)%mod+mod)%mod;
}

const int MAXP=40;
const int INF=(1<<30)-1;

int p,q,r,d,S,T;
int v[MAXP+5][MAXP+5][MAXP+5];

struct edge{
    int to,nxt,w;
    edge(){}
    edge(const int T,const int N,const int W):to(T),nxt(N),w(W){}
}e[(MAXP*MAXP*MAXP*10)+5];
int tail[MAXP*MAXP*MAXP*MAXP+5],ecnt;
int cur[MAXP*MAXP*MAXP*MAXP+5];
inline void add_edge(const int u,const int v,const int w){
    // printf("add_edge:>u == %d, v == %d, w == %d\n",u,v,w);
    e[++ecnt]=edge(v,tail[u],w);tail[u]=ecnt;
}

inline int id(const int x,const int y,const int z){return x*40*40+y*40+z;}

inline bool inside(const int x,const int y,const int z){
    return 0<x && x<=p && 0<y && y<=q && 0<z && z<=r;
}

int dis[MAXP*MAXP*MAXP*MAXP+5];
inline bool bfs(){
    rep(i,0,id(p,q,r)+1)dis[i]=-1;
    dis[S]=0;
    queue<int>Q;Q.push(S);
    while(!Q.empty()){
        int u=Q.front();Q.pop();
        for(int i=tail[u],v;i;i=e[i].nxt){
            v=e[i].to;
            if(dis[v]!=-1 || e[i].w<=0)continue;
            dis[v]=dis[u]+1;
            Q.push(v);
        }
    }
    return dis[T]!=-1;
}

int dfs(const int u,int inflow){
    if(u==T)return inflow;
    int sum=0,tmp,v;
    for(int& i=cur[u];i;i=e[i].nxt){
        v=e[i].to;
        if(dis[v]!=dis[u]+1 || e[i].w<=0)continue;
        tmp=dfs(v,Min(inflow-sum,e[i].w));
        e[i].w-=tmp,e[i^1].w+=tmp;
        if((sum+=tmp)>=inflow)break;
    }
    return sum;
}

inline int dinic(){
    int max_flow=0;
    while(bfs()){
        rep(i,0,id(p,q,r)+1)cur[i]=tail[i];
        max_flow+=dfs(S,INF);
    }
    return max_flow;
}

signed main(){
#ifdef FILEOI
    freopen("file.in","r",stdin);
    freopen("file.out","w",stdout);
#endif
    ecnt=1;
    qread(p,q,r,d);
    S=0,T=id(p,q,r)+1;
    // printf("S == %d, T == %d\n",S,T);
    rep(k,1,r)rep(i,1,p)rep(j,1,q)qread(v[i][j][k]);
    rep(i,1,p)rep(j,1,q){
        add_edge(S,id(i,j,1),INF);
        add_edge(id(i,j,1),S,0);
    }
    rep(k,1,r-1)rep(i,1,p)rep(j,1,q){
        add_edge(id(i,j,k),id(i,j,k+1),v[i][j][k]);
        add_edge(id(i,j,k+1),id(i,j,k),0);
        if(k<=d)continue;
        // puts("the special edge:");
        if(inside(i+1,j,k-d)){
            add_edge(id(i,j,k),id(i+1,j,k-d),INF);
            add_edge(id(i+1,j,k-d),id(i,j,k),0);
        }
        if(inside(i-1,j,k-d)){
            add_edge(id(i,j,k),id(i-1,j,k-d),INF);
            add_edge(id(i-1,j,k-d),id(i,j,k),0);
        }
        if(inside(i,j+1,k-d)){
            add_edge(id(i,j,k),id(i,j+1,k-d),INF);
            add_edge(id(i,j+1,k-d),id(i,j,k),0);
        }
        if(inside(i,j-1,k-d)){
            add_edge(id(i,j,k),id(i,j-1,k-d),INF);
            add_edge(id(i,j-1,k-d),id(i,j,k),0);
        }
        // puts("-------------end-------------");
    }
    rep(i,1,p)rep(j,1,q){
        add_edge(id(i,j,r),T,v[i][j][r]);
        add_edge(T,id(i,j,r),0);
        if(r<=d)continue;
        // puts("the special edge:");
        if(inside(i+1,j,r-d)){
            add_edge(id(i,j,r),id(i+1,j,r-d),INF);
            add_edge(id(i+1,j,r-d),id(i,j,r),0);
        }
        if(inside(i-1,j,r-d)){
            add_edge(id(i,j,r),id(i-1,j,r-d),INF);
            add_edge(id(i-1,j,r-d),id(i,j,r),0);
        }
        if(inside(i,j+1,r-d)){
            add_edge(id(i,j,r),id(i,j+1,r-d),INF);
            add_edge(id(i,j+1,r-d),id(i,j,r),0);
        }
        if(inside(i,j-1,r-d)){
            add_edge(id(i,j,r),id(i,j-1,r-d),INF);
            add_edge(id(i,j-1,r-d),id(i,j,r),0);
        }
        // puts("-------------end-------------");
    }
    int ret=dinic();
    writc(ret,'\n');
    return 0;
}

一道十分好的题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值