zoukankan      html  css  js  c++  java
  • 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
    ",ans);
    
        return 0;
    }
  • 相关阅读:
    集合介绍,创建,添加,删除。
    字典简介、操作、内置函数、练习题
    git教程——简单总结
    前端性能优化总结
    小米2018春招实习笔试题总结
    浏览器缓存控制 以及 在url框中回车、F5 和 Ctrl + F5的区别
    携程2018春招实习前端开发笔试题分享
    不同方式实现两列布局
    移动端开发-viewport与媒体查询
    华为2018春招前端开发实习生笔试题分享
  • 原文地址:https://www.cnblogs.com/zbtrs/p/8605251.html
Copyright © 2011-2022 走看看