bzoj3144: [Hnoi2013]切糕【最小割】

Description

这里写图片描述

Input

第一行是三个正整数P,Q,R,表示切糕的长P、 宽Q、高R。第二行有一个非负整数D,表示光滑性要求。接下来是R个P行Q列的矩阵,第z个 矩阵的第x行第y列是v(x,y,z) (1≤x≤P, 1≤y≤Q, 1≤z≤R)。
100%的数据满足P,Q,R≤40,0≤D≤R,且给出的所有的不和谐值不超过1000。

Output

仅包含一个整数,表示在合法基础上最小的总不和谐值。

Sample Input

2 2 2

1

6 1

6 1

2 6

2 6

Sample Output

6

HINT

最佳切面的f为f(1,1)=f(2,1)=2,f(1,2)=f(2,2)=1

解题思路:

考虑最小割。
首先肯定是(i,j,k)向(i,j,k+1)连f(i,j,k)的边。
如何满足相距不超过D的条件?也就是割了(i,j,k)这条边,必须要割掉(i’,j’,k-D~k+D)中的一条边,所以(i,j,k)向(i’,j’,k-D),(i’,j’,k+D+1)向(i’,j’,k+1)连INF的边即可。

#include<bits/stdc++.h>
using namespace std;

int getint()
{
    int i=0,f=1;char c;
    for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
    if(c=='-')f=-1,c=getchar();
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f; 
}

const int N=100005,M=1000005,INF=0x3f3f3f3f;
const int fx[4]={-1,0,1,0};
const int fy[4]={0,-1,0,1};
int P,Q,R,D,S,T,n,p[45][45][45];
int tot=1,dis[N],cur[N],first[N],nxt[M],to[M],cap[M];
queue<int>q;

void add(int x,int y,int z)
{
    nxt[++tot]=first[x],first[x]=tot,to[tot]=y,cap[tot]=z;
    nxt[++tot]=first[y],first[y]=tot,to[tot]=x,cap[tot]=0;
}

bool bfs()
{
    while(!q.empty())q.pop();
    for(int i=S;i<=T;i++)dis[i]=-1,cur[i]=first[i];
    q.push(S),dis[S]=0;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int e=first[u];e;e=nxt[e])
        {
            int v=to[e];
            if(dis[v]==-1&&cap[e])
            {
                dis[v]=dis[u]+1,q.push(v);
                if(v==T)return true;
            }
        }
    }
    return false;
}

int dinic(int u,int flow)
{
    if(u==T)return flow;
    int res=0;
    for(int &e=cur[u];e;e=nxt[e])
    {
        int v=to[e];
        if(dis[v]==dis[u]+1&&cap[e])
        {
            int det=dinic(v,min(flow-res,cap[e]));
            cap[e]-=det,cap[e^1]+=det;
            res+=det;if(res==flow)break;
        }
    }
    if(res<flow)dis[u]=-1;
    return res;
}

int main()
{
    //freopen("lx.in","r",stdin);
    P=getint(),Q=getint(),R=getint(),D=getint();
    for(int i=1;i<=P;i++)
        for(int j=1;j<=Q;j++)
            for(int k=1;k<=R;k++)p[i][j][k]=++n;
    S=0,T=++n;
    for(int k=1;k<=R;k++)
        for(int i=1;i<=P;i++)
            for(int j=1;j<=Q;j++)
            {
                int z=getint();
                if(k==1)add(S,p[i][j][k],INF);
                k==R?add(p[i][j][k],T,z):add(p[i][j][k],p[i][j][k+1],z);
                for(int l=0;l<4;l++)
                {
                    int di=i+fx[l],dj=j+fy[l];
                    if(di<1||di>P||dj<1||dj>Q)continue;
                    k>D?add(p[i][j][k],p[di][dj][k-D],INF):add(p[i][j][k],p[di][dj][1],INF);
                }
            }
    int ans=0;
    while(bfs())ans+=dinic(S,INF);
    cout<<ans;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值