bzoj3144 [Hnoi2013]切糕

3144: [Hnoi2013]切糕

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 2380  Solved: 1301
[Submit][Status][Discuss]

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

分析:一道我不会但是很经典的套路题.

   一开始的想法是dp,但是要记录的状态实在是太多了,复杂度太高.

   然后想到的是贪心.但是想不到贪心的方法.题目牵扯到的条件太多,没有办法贪心.

   怎么办呢?网络流!

   对于网络流题目的思考方法:先考虑最大流模型,再来考虑费用流模型. 如果有限制则考虑容量限制和最小割限制.

   先尝试用最大流.怎么表示限制呢? 容量限制?联想到流量平衡模型(传送门).但是这道题根本就没办法按照那个模型的套路来.那就只有考虑最小割限制咯.

   怎么用最小割来表示限制呢?一般连边权为inf的边使得不会割掉这条边. 因为最小割最终要使得源点和汇点不连通,可以利用这一条性质.

   考虑本题的限制,先把绝对值给去掉,每个点只考虑四周比它高度小的点,也就是f(x,y) - f(x',y') ≤ D.  那么我们的任务就是割掉一条f(x,y)的边后,割掉f(x',y') (f(x',y')不满足要求)后源点和汇点仍然连通.  怎么做到这一点呢?通过加边权为inf的边就能做到了.

   观察这张图. 这是一个立体结构. 如果割掉红色的边,再割掉绿色的边,ST还可以连通,要的就是这个效果. 如果不割绿边,而是割绿边上面的边,ST就不连通了.正好对应了限制.

   那么建图方法就出来了:

   1.从S点向第1层的所有点连容量为inf的边.

   2.从(i,j)的第k层连(i,j)的第k+1层容量为f(i,j,k)的边,

   3.从第h + 1层的所有点向T连容量为inf的边.

   4.从(i,j,k)向(i',j',k - D)连容量为inf的边.

   所求的最小割即是答案.

   这是一类经典模型:解决这类题目的方法就是通过最小割满足限制.

 

#include <cstdio>
#include <queue>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int inf = 0x7fffffff,maxn = 300010,dx[] = {0,0,1,-1},dy[] = {1,-1,0,0};
int n,m,h,id[45][45][45],cnt,S,T,head[maxn],to[maxn],nextt[maxn],w[maxn],tot = 2;
int d[maxn],D,ans;

void add(int x,int y,int z)
{
    w[tot] = z;
    to[tot] = y;
    nextt[tot] = head[x];
    head[x] = tot++;

    w[tot] = 0;
    to[tot] = x;
    nextt[tot] = head[y];
    head[y] = tot++;
}

void build()
{
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            add(S,id[1][i][j],inf);
    for (int k = 1; k <= h; k++)
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; j++)
            {
                int x;
                scanf("%d",&x);
                add(id[k][i][j],id[k + 1][i][j],x);
            }
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            add(id[h + 1][i][j],T,inf);
    for (int k = D + 1; k <= h + 1; k++)
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; j++)
                for (int p = 0; p < 4; p++)
                {
                    int nx = dx[p] + i,ny = dy[p] + j;
                    if (id[k - D][nx][ny])
                        add(id[k][i][j],id[k - D][nx][ny],inf);
                }
}

bool bfs()
{
    memset(d,-1,sizeof(d));
    d[S] = 0;
    queue <int> q;
    q.push(S);
    while (!q.empty())
    {
        int u = q.front();
        q.pop();
        if (u == T)
            return true;
        for (int i = head[u];i;i = nextt[i])
        {
            int v = to[i];
            if (w[i] && d[v] == -1)
            {
                d[v] = d[u] + 1;
                q.push(v);
            }
        }
    }
    return false;
}

int dfs(int u,int f)
{
    if (u == T)
        return f;
    int res = 0;
    for (int i = head[u];i;i = nextt[i])
    {
        int v = to[i];
        if (w[i] && d[v] == d[u] + 1)
        {
            int temp = dfs(v,min(f - res,w[i]));
            w[i] -= temp;
            w[i ^ 1] += temp;
            res += temp;
            if (res == f)
                return res;
        }
    }
    if (!res)
        d[u] = -1;
    return res;
}

void dinic()
{
    while(bfs())
        ans += dfs(S,inf);
}

int main()
{
    scanf("%d%d%d",&n,&m,&h);
    scanf("%d",&D);
    for (int i = 1; i <= h + 1; i++)
        for (int j = 1; j <= n; j++)
            for (int k = 1; k <= m; k++)
                id[i][j][k] = ++cnt;
    S = ++cnt;
    T = ++cnt;
    build();
    dinic();
    printf("%d\n",ans);

    return 0;
}
posted @ 2018-03-19 21:58  zbtrs  阅读(294)  评论(0编辑  收藏  举报