1. 程式人生 > >bzoj 3144 切糕 —— 最小割

bzoj 3144 切糕 —— 最小割

tin pop pac namespace bre continue clas memcpy getch

題目: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; }

bzoj 3144 切糕 —— 最小割