#网络流,dinic,最小割#洛谷 3227 [HNOI2013]切糕
题目大意
\(P\)行\(Q\)列的楼房高度均为\(R\),每一层改造要花费一定的金钱,
每个楼房都要挑选有且仅有一层进行改造,并且相邻两个楼房改造位置的相对高度不能超过\(D\),
问最小花费
分析
原题目更能看出是最小割,但是这样题意更能懂一些,
可以建一个分层图,建一个虚拟层,这样把点换为边,
接着源点连第一层,虚拟层连汇点,容量无穷大
但是相对高度怎么搞,就是让它割不掉嘛
若两个点\((x_1,y_1),(x_2,y_2)\)相邻,
那么\((x_1,y_1,k)\)与\((x_2,y_2,k+D)\)连一条边,容量无穷大,就好了
代码
#include <cstdio>
#include <cctype>
#include <queue>
#define rr register
using namespace std;
const int inf=1e7,N=66011;
struct node{int y,w,next;}e[N*11];
const int dx[4]={0,1,0,-1},dy[4]={1,0,-1,0};
int ls[N],dis[N],P,Q,R,D,ans,s,t,k=1; bool v[N];
inline signed iut(){
rr int ans=0,f=1; rr char c=getchar();
while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans*f;
}
inline void add(int x,int y,int w){
e[++k]=(node){y,w,ls[x]}; ls[x]=k;
e[++k]=(node){x,0,ls[y]}; ls[y]=k;
}
inline signed bfs(int s){
for (rr int i=1;i<=t;++i) dis[i]=0;
queue<int>q; q.push(s); dis[s]=1;
while (q.size()){
rr int x=q.front(); q.pop();
for (rr int i=ls[x];i;i=e[i].next)
if (e[i].w>0&&!dis[e[i].y]){
dis[e[i].y]=dis[x]+1;
if (e[i].y==t) return 1;
q.push(e[i].y);
}
}
return 0;
}
inline signed dfs(int x,int now){
if (x==t||!now) return now;
rr int rest=0,f;
for (rr int i=ls[x];i;i=e[i].next)
if (e[i].w>0&&dis[e[i].y]==dis[x]+1){
rest+=(f=dfs(e[i].y,min(now-rest,e[i].w)));
e[i].w-=f; e[i^1].w+=f;
if (now==rest) return rest;
}
if (!rest) dis[x]=0;
return rest;
}
signed main(){
P=iut(),Q=iut(),R=iut(),
D=iut(),s=P*Q*(R+1)+1,t=s+1;
for (rr int i=1;i<=R;++i)
for (rr int j=1;j<=P;++j)
for (rr int k=1;k<=Q;++k)
add((i*P+j-P-1)*Q+k,(i*P+j-1)*Q+k,iut());
for (rr int i=1;i<=P;++i)
for (rr int j=1;j<=Q;++j)
add(s,(i-1)*Q+j,inf),add((R*P+i-1)*Q+j,t,inf);
for (rr int i=1;i<=P;++i)
for (rr int j=1;j<=Q;++j)
for (rr int u=0;u<4;++u){
rr int x=i+dx[u],y=j+dy[u];
if (x<1||x>P||y<1||y>Q) continue;
for (rr int h=D;h<=R;++h)
add((h*P+i-1)*Q+j,(h*P-D*P+x-1)*Q+y,inf);
}
while (bfs(s)) ans+=dfs(s,inf);
return !printf("%d",ans);
}