「题解」「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 R1≤f(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)、(i−1,j)、(i,j+1)、(i,j−1) 的竖列。

但是有一个限制,对于相邻的竖列,在他们上所选择的点的高度差不超过 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 D∣f(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 的限制。

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

假设我们有这样一个图:

其中, 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} 似乎没有影响。

但是又有一个问题:如果我们选的点比 p3p3p3 高了 DDD 呢?

其实这个问题是一样的:我们连接 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,k−d)

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

对于 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;
}

一道十分好的题

「题解」「HNOI2013」切糕的更多相关文章

  1. 「ZJOI2019」&「十二省联考 2019」题解索引

    「ZJOI2019」&「十二省联考 2019」题解索引 「ZJOI2019」 「ZJOI2019」线段树 「ZJOI2019」Minimax 搜索 「十二省联考 2019」 「十二省联考 20 ...

  2. 「HNOI2013」游走

    「HNOI2013」游走 题目描述 一个无向连通图,顶点从 \(1\) 编号到 \(N\) ,边从 \(1\) 编号到 \(M\) .小 \(Z\) 在该图上进行随机游走,初始时小 \(Z\) 在 \ ...

  3. 「题解」「美团 CodeM 资格赛」跳格子

    目录 「题解」「美团 CodeM 资格赛」跳格子 题目描述 考场思路 思路分析及正解代码 「题解」「美团 CodeM 资格赛」跳格子 今天真的考自闭了... \(T1\) 花了 \(2h\) 都没有搞 ...

  4. 「题解」JOIOI 王国

    「题解」JOIOI 王国 题目描述 考场思考 正解 题目描述 点这里 考场思考 因为时间不太够了,直接一上来就着手暴力.但是本人太菜,居然暴力爆 000 ,然后当场自闭- 一气之下,发现对 60pts ...

  5. 【题解】「P6832」[Cnoi2020]子弦

    [题解]「P6832」[Cnoi2020]子弦第一次写月赛题解( 首先第一眼看到这题,怎么感觉要用 \(\texttt{SAM}\) 什么高科技的?结果一仔细读题,简单模拟即可. 我们不难想出,出现最 ...

  6. 「题解报告」 P3167 [CQOI2014]通配符匹配

    「题解报告」 P3167 [CQOI2014]通配符匹配 思路 *和?显然无法直接匹配,但是可以发现「通配符个数不超过 \(10\) 」,那么我们可以考虑分段匹配. 我们首先把原字符串分成多个以一个通 ...

  7. 「bzoj1003」「ZJOI2006」物流运输 最短路+区间dp

    「bzoj1003」「ZJOI2006」物流运输---------------------------------------------------------------------------- ...

  8. 「bzoj1925」「Sdoi2010」地精部落 (计数型dp)

    「bzoj1925」「Sdoi2010」地精部落---------------------------------------------------------------------------- ...

  9. 「BZOJ1924」「SDOI2010」 所驼门王的宝藏 tarjan + dp(DAG 最长路)

    「BZOJ1924」[SDOI2010] 所驼门王的宝藏 tarjan + dp(DAG 最长路) -------------------------------------------------- ...

随机推荐

  1. 吴裕雄 python 机器学习——半监督学习LabelSpreading模型

    import numpy as np import matplotlib.pyplot as plt from sklearn import metrics from sklearn import d ...

  2. glog与gflags的windows编译

    参考博客:https://kezunlin.me/post/bb64e398/

  3. NABCD model作业

    1)N(Need需求) 随着人类生活的快速发展,给人们带来了许多的便利,同时也给我们带来了一些麻烦,而我的拼图这个小游戏可以在人们在无聊时玩一玩,也可以给小孩子开发智力. 2)A(Approach做法 ...

  4. const和defin区别

    (1)类型的安全性检查:const常量有数据类型,而define定义宏常量没有数据类型.则编译器可以对前者进行类型安全检查,而对后者只进行字符替换,没有类型安全检查(字符替换时可能会产生意料不到的错误 ...

  5. Anaconda"无法定位程序输入点 OPENSSL_sk_new_reserve 于动态链接库Anaconda3\Library\bin\libssl-1_1-x64.dll上"的解决办法

    Anaconda"无法定位程序输入点 OPENSSL_sk_new_reserve 于动态链接库Anaconda3\Library\bin\libssl-1_1-x64.dll上" ...

  6. idea项目更改git地址

    第一步:idea打开项目,菜单栏找VCS - Git - Remotes 点进去,弹出对话框,选中,点击编辑 弹出编辑框,更改地址,点击ok 弹出输入账号密码编辑框,输入自己的账号密码,点击确认 完成 ...

  7. 多进程manager共享

    使用manager在进程之间事项共享数据. 栗子: 主进程调用manager,创建一个字典d和一个列表l,启动十个子进程,每个子进程都向d和l中放数据 from multiprocessing imp ...

  8. makefile的书写

    工作中会遇到makefile的书写,有必要做一些笔记.尽管网上有”万能模板“可以使用,但我觉得还是有必要从最基础的写起. 平常在windows下开发,不知道自己用过makefile,其实大部分IDE里 ...

  9. Python(二) isinstance

    原文链接: http://www.baike.com/wiki/isinstance&prd=jinshan https://www.liaoxuefeng.com/wiki/00143160 ...

  10. redis(一)动态字符串

    redis 动态字符串 概述 Sda(Simple Dynamic String) 简单动态字符串是 redis中用来表示字符串的结构,而不是传统 C 字符串. 主要的特点就是Sda要做到高效和 二进 ...