bzoj 3144 切糕 —— 最小割
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3144
每个点拆成 R 个,连成一条链,边上是权值,割掉代表选这一层;
然后每个点的第 t 层向四周的点的第 t-d 层连边,就能达到选了第 i 条边,则四周的点必须选 i-d ~ T 范围的边,而对方反过来一连,就限制在 i-d ~ i+d 了;
竟然因为忘记 ct=1 而调了一小时呵呵...
代码如下:
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; int const xn=64005,xm=xn*10,inf=1e9; int n,m,H,hd[xn],ct=1,to[xm],nxt[xm],c[xm],dis[xn],cur[xn],S,T; queue<int>q; int rd() { int ret=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return f?ret:-ret; } void ade(int x,int y,int z){to[++ct]=y; nxt[ct]=hd[x]; hd[x]=ct; c[ct]=z;} void add(int x,int y,int z){ade(x,y,z); ade(y,x,0);} int id(int x,int y,int k){return ((x-1)*m+y-1)*H+k;} bool bfs() { for(int i=S;i<=T;i++)dis[i]=0; dis[S]=1; q.push(S); while(q.size()) { int x=q.front(); q.pop(); for(int i=hd[x],u;i;i=nxt[i]) if(!dis[u=to[i]]&&c[i])dis[u]=dis[x]+1,q.push(u); } return dis[T]; } int dfs(int x,int fl) { if(x==T)return fl; int ret=0; for(int &i=cur[x],u;i;i=nxt[i]) { if(dis[u=to[i]]!=dis[x]+1||!c[i])continue; int tmp=dfs(u,min(fl-ret,c[i])); if(!tmp)dis[u]=0; c[i]-=tmp; c[i^1]+=tmp; ret+=tmp; if(ret==fl)break; } return ret; } int main() { n=rd(); m=rd(); H=rd(); int d=rd(); S=0; T=n*m*H+1; for(int t=1;t<=H;t++) for(int i=1;i<=n;i++) for(int j=1,x;j<=m;j++) { x=rd(); int nw=id(i,j,t); if(t==1)add(S,nw,x); else add(id(i,j,t-1),nw,x); if(t==H)add(nw,T,inf); if(t<=d)continue; int k=t-d;// if(i>1)add(nw,id(i-1,j,k),inf); if(j>1)add(nw,id(i,j-1,k),inf); if(i<n)add(nw,id(i+1,j,k),inf); if(j<m)add(nw,id(i,j+1,k),inf); } int ans=0; while(bfs()) { memcpy(cur,hd,sizeof hd); ans+=dfs(S,inf); } printf("%d\n",ans); return 0; }