P3227 [HNOI2013]切糕 最小割
阿新 • • 發佈:2021-01-12
題意:
分析:
又一次被題解的智慧所折服,orz
其實按著思路推過來還是挺符合邏輯的,一看資料範圍這麼小,要麼 \(DP\) 要麼 網路流, \(DP\) 不太現實狀態數過多無法轉移,那麼就是網路瘤了
首先我們去掉毒瘤的光滑限制,也就是說任意兩個相鄰的列沒有高度差要求,這樣直接選出每一列最小的一個,像這種取最值的問題轉化成網路流的建模就是最小割,建一個超級源向每一列的第一個連邊流量為 \(inf\) ,再建一個超級匯點,每列最後一個與超級匯連邊流量為 \(inf\) ,這樣保證了源匯的邊不會被割掉,由於題目給的是點值,我們將點值壓到邊上,\((x,y,z)\) 向 \((x,y,z+1)\)
好,我們開始考慮怎麼加上光滑限制,我們考慮什麼情況下兩個邊不會被同時割掉,當且僅當,它們兩所在的路徑還有可能被增廣的時候,也就是說我們需要保證他們兩相連,且它們兩還能形成一條增廣路,那麼在上面網路流模型的基礎上我們由 \((x_1,y_1,z_1)\) 向 \((x_2,y_2,z_1-d)\) 連一條流量為 \(inf\) 的邊,這樣這條邊不會被割掉保證了它們兩一定相連,同時在它們兩被割掉的時候還形成了一條新的增廣路徑 : \(st\to(x_1,y_1,z_1)\to (x_2,y_2,z_1-d)\to(x_2,y_2,r+1)\to ed\)
程式碼:
吐槽一句,題解程式碼過於毒瘤,建模看不懂/kk
#include<bits/stdc++.h> #define pii pair<int,int> #define mk(x,y) make_pair(x,y) #define lc rt<<1 #define rc rt<<1|1 #define pb push_back #define fir first #define sec second using namespace std; namespace zzc { inline int read() { int x=0,f=1;char ch=getchar(); while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();} while (isdigit(ch)){x=x*10+ch-48;ch=getchar();} return x*f; } const int maxn = 1e5+5; const int maxm = 1e6+5; const int inf = 0x3f3f3f; int p,q,r,st,ed,cnt=1,ans,d; int head[maxn],dep[maxn],v[41][41][41]; int dx[4]={0,1,0,-1},dy[4]={1,0,-1,0}; struct edge { int to,nxt,w; }e[maxm]; queue<int> qu; void add(int u,int v,int w) { e[++cnt].to=v; e[cnt].w=w; e[cnt].nxt=head[u]; head[u]=cnt; } void add_edge(int u,int v,int w) { add(u,v,w);add(v,u,0); } int id(int x,int y,int z) { return (z-1)*p*q+(x-1)*q+y; } bool check(int x,int y) { return x>=1&&x<=p&&y>=1&&y<=q; } bool bfs() { for(int i=st;i<=ed;i++) dep[i]=-1; qu.push(st);dep[st]=0; while(!qu.empty()) { int u=qu.front();qu.pop(); for(int i=head[u];i;i=e[i].nxt) { int v=e[i].to; if(e[i].w&&dep[v]==-1) { dep[v]=dep[u]+1; qu.push(v); } } } return dep[ed]!=-1; } int dfs(int u,int flow) { if(u==ed) return flow; int used=0,w; for(int i=head[u];i;i=e[i].nxt) { int v=e[i].to; if(e[i].w&&dep[v]==dep[u]+1) { w=dfs(v,min(e[i].w,flow-used)); e[i].w-=w; e[i^1].w+=w; used+=w; if(used==flow) return used; } } if(!used) dep[u]=-1; return used; } void dinic() { int fl; while(bfs()) { fl=dfs(st,inf); ans+=fl; } } void work() { p=read();q=read();r=read();d=read();st=0;ed=p*q*(r+1)+1; for(int z=1;z<=r;z++) { for(int x=1;x<=p;x++) { for(int y=1;y<=q;y++) { v[x][y][z]=read(); add_edge(id(x,y,z),id(x,y,z+1),v[x][y][z]); } } } for(int z=1;z<=r+1;z++) { for(int x=1;x<=p;x++) { for(int y=1;y<=q;y++) { if(z==1) add_edge(st,id(x,y,z),inf); else if(z==r+1) add_edge(id(x,y,z),ed,inf); if(z>=d+1) { for(int i=0;i<4;i++) { int tx=x+dx[i],ty=y+dy[i]; if(check(tx,ty)) add_edge(id(x,y,z),id(tx,ty,z-d),inf); } } } } } dinic(); printf("%d\n",ans); } } int main() { zzc::work(); return 0; }