2 条题解
-
0
题解
思路分析
这是一道经典的 最小割 + 网络流 的三维网格切割问题。
问题建模
- 在 的三维网格中选择切面
- 切面是函数 ,表示每个纵轴的切割高度
- 相邻纵轴的切割高度差
- 最小化切割点的不和谐值之和
核心思路
1. 最小割建模
这是一个经典的闭合子图问题,可以转化为最小割。
将三维空间看作分层图:
- 每个点 拆成一个节点
- 选择切面等价于在每个纵轴上选择一个高度
2. 网络流构图(关键)
核心思想:将切面问题转化为最小割。
建图原理:
- 每个纵轴 必须选择恰好一个高度 作为切割点
- 选择高度 意味着切断该纵轴在 和 之间的"连接"
- 所有选择的切割点构成一个"割"
具体构图:
-
纵轴内部( 个纵轴):
- 对每个 ,创建节点
- 连边:()
- 割掉这条边 选择 作为切割高度
-
源点和汇点:
- (所有纵轴的底部)
- (所有纵轴的顶部)
- 保证每个纵轴都被"切穿"
-
相邻纵轴约束(保证光滑性 ):
- 如果 (曼哈顿距离为1)
- 连边:()
- 含义:如果 在 或更高处,则 不能在 以下
- 等价于:,即
为什么是最小割?
- 每个纵轴必须恰好割一条边(选择切割高度)
- 相邻约束边容量为 ,不会被割
- 最小割 = 最小切割代价
3. Dinic 求最小割
使用 Dinic 算法求最小割,即为最小总不和谐值。
算法步骤
-
建图:
- 创建 个节点
- 添加源点和汇点
- 按规则连边
-
Dinic 算法:
- BFS 分层
- DFS 增广
- 求最大流(= 最小割)
-
输出答案
复杂度分析
- 节点数:
- 边数:
- Dinic 时间复杂度:
- 总时间复杂度:
- 空间复杂度:
#include<bits/stdc++.h> using namespace std; typedef long long ll; #define endl '\n' const int MAXP=59,N=MAXP*MAXP*MAXP*2,M=2e6+9; const ll INF=1e13; int p,q,r; int D; int v[MAXP][MAXP][MAXP]; int head[N],nxt[M],to[M]; ll w[M]; int s,t; int dx[]={0,0,1,-1},dy[]={1,-1,0,0}; ll ans=0; int d[N]; int cnt=-1; int now[N]; int chg(int x,int y,int z){ return x*q*(r+1)+y*(r+1)+z; } void add(int u,int v,ll k){ nxt[++cnt]=head[u]; head[u]=cnt; w[cnt]=k,to[cnt]=v; nxt[++cnt]=head[v]; head[v]=cnt; w[cnt]=0,to[cnt]=u; return; } void print_edge(){ for(int i=s;i<=t;i++){ cout<<i<<endl; for(int j=head[i];j!=-1;j=nxt[j]) cout<<to[j]<<" "<<w[j]<<endl; } return; } bool bfs(){ for(int i=s;i<=t;i++)d[i]=0; d[s]=1; queue<int> q; q.push(s); while(!q.empty()){ int f=q.front();q.pop(); if(f==t)return 1; for(int i=head[f];i!=-1;i=nxt[i]){ int v=to[i]; if(d[v]||!w[i])continue; d[v]=d[f]+1; q.push(v); } } return 0; } ll dfs(int u,ll f){ if(u==t)return f; ll sum=0; for(int i=now[u];i!=-1;i=nxt[i]){ int v=to[i]; if(d[v]!=d[u]+1||!w[i])continue; ll tmp=dfs(v,min(w[i],f-sum)); if(tmp){ w[i]-=tmp,w[i^1]+=tmp; sum+=tmp; now[u]=i; } else d[v]=-1; } return sum; } void Dinic(){ while(bfs()){ for(int i=s;i<=t;i++)now[i]=head[i]; ans+=dfs(s,1e17); } return; } int main(){ ios::sync_with_stdio(0); cin.tie(nullptr); cin>>p>>q>>r; cin>>D; s=0,t=chg(p,q,r+1)+1; for(int i=s;i<=t;i++)head[i]=-1; for(int k=1;k<=r;k++)for(int i=1;i<=p;i++)for(int j=1;j<=q;j++)cin>>v[i][j][k]; for(int x=1;x<=p;x++) for(int y=1;y<=q;y++){ add(s,chg(x,y,1),INF); add(chg(x,y,r+1),t,INF); for(int z=1;z<=r;z++) add(chg(x,y,z),chg(x,y,z+1),v[x][y][z]); for(int z=D+1;z<=r+1;z++) for(int i=0;i<4;i++){ int nx=x+dx[i],ny=y+dy[i]; if(1<=nx&&nx<=p && 1<=ny&&ny<=q) add(chg(x,y,z),chg(nx,ny,z-D),INF); } } // print_edge(); Dinic(); cout<<ans<<endl; return 0; } -
0
题解
把切面看成从柱状体中挑选出每根纵轴的一个高度,同时对相邻纵轴施加 “高度差不超过 ” 的限制。经典做法是把选取过程转化为一次最小割:对于每根纵轴 ,建出从底到顶共 个节点,依次用容量为 的边把高度 与 连接起来,再用无穷大容量的边把底部连向源点、顶部连向汇点。这样一来,割掉的第一条垂直边恰好表示切面在该纵轴的高度,且总代价正好是所有被切断的竖向边容量之和。
为了保证相邻纵轴的高度差不超过 ,如果在 上选择高度 ,那么相邻列 的高度必须属于 。在网络中可以通过额外的无穷大边实现:对于每个 、每个 ,向四个相邻纵轴的节点 连一条容量为无穷的边。这样,如果两根纵轴的切点相差超过 ,割集就会被迫同时割掉这些无穷大边,从而不可行。
最终在这个图上跑一次 Dinic 求最小割即可。割值就是满足所有约束的切面上不和谐值的最小总和。
#include<bits/stdc++.h> using namespace std; typedef long long ll; #define endl '\n' const int MAXP=59,N=MAXP*MAXP*MAXP*2,M=2e6+9; const ll INF=1e13; int p,q,r; int D; int v[MAXP][MAXP][MAXP]; int head[N],nxt[M],to[M]; ll w[M]; int s,t; int dx[]={0,0,1,-1},dy[]={1,-1,0,0}; ll ans=0; int d[N]; int cnt=-1; int now[N]; int chg(int x,int y,int z){ return x*q*(r+1)+y*(r+1)+z; } void add(int u,int v,ll k){ nxt[++cnt]=head[u]; head[u]=cnt; w[cnt]=k,to[cnt]=v; nxt[++cnt]=head[v]; head[v]=cnt; w[cnt]=0,to[cnt]=u; return; } void print_edge(){ for(int i=s;i<=t;i++){ cout<<i<<endl; for(int j=head[i];j!=-1;j=nxt[j]) cout<<to[j]<<" "<<w[j]<<endl; } return; } bool bfs(){ for(int i=s;i<=t;i++)d[i]=0; d[s]=1; queue<int> q; q.push(s); while(!q.empty()){ int f=q.front();q.pop(); if(f==t)return 1; for(int i=head[f];i!=-1;i=nxt[i]){ int v=to[i]; if(d[v]||!w[i])continue; d[v]=d[f]+1; q.push(v); } } return 0; } ll dfs(int u,ll f){ if(u==t)return f; ll sum=0; for(int i=now[u];i!=-1;i=nxt[i]){ int v=to[i]; if(d[v]!=d[u]+1||!w[i])continue; ll tmp=dfs(v,min(w[i],f-sum)); if(tmp){ w[i]-=tmp,w[i^1]+=tmp; sum+=tmp; now[u]=i; } else d[v]=-1; } return sum; } void Dinic(){ while(bfs()){ for(int i=s;i<=t;i++)now[i]=head[i]; ans+=dfs(s,1e17); } return; } int main(){ ios::sync_with_stdio(0); cin.tie(nullptr); cin>>p>>q>>r; cin>>D; s=0,t=chg(p,q,r+1)+1; for(int i=s;i<=t;i++)head[i]=-1; for(int k=1;k<=r;k++)for(int i=1;i<=p;i++)for(int j=1;j<=q;j++)cin>>v[i][j][k]; for(int x=1;x<=p;x++) for(int y=1;y<=q;y++){ add(s,chg(x,y,1),INF); add(chg(x,y,r+1),t,INF); for(int z=1;z<=r;z++) add(chg(x,y,z),chg(x,y,z+1),v[x][y][z]); for(int z=D+1;z<=r+1;z++) for(int i=0;i<4;i++){ int nx=x+dx[i],ny=y+dy[i]; if(1<=nx&&nx<=p && 1<=ny&&ny<=q) add(chg(x,y,z),chg(nx,ny,z-D),INF); } } // print_edge(); Dinic(); cout<<ans<<endl; return 0; }
- 1
信息
- ID
- 3259
- 时间
- 3000ms
- 内存
- 256MiB
- 难度
- 8
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者