zoukankan      html  css  js  c++  java
  • P3049 [USACO12MAR]园林绿化Landscaping

    贪心啦贪心,好久没 贪心啦~

    这道园林绿化教你做人啦~

    题目描述

    Farmer John is building a nicely-landscaped garden, and needs to move a large amount of dirt in the process.

    The garden consists of a sequence of N flowerbeds (1 <= N <= 100), where flowerbed i initially contains A_i units of dirt. Farmer John would like to re-landscape the garden so that each flowerbed i instead contains B_i units of dirt. The A_i's and B_i's are all integers in the range 0..10.

    To landscape the garden, Farmer John has several options: he can purchase one unit of dirt and place it in a flowerbed of his choice for XX. He can remove one unit of dirt from a flowerbed of his choice and have it shipped away for YY. He can also transport one unit of dirt from flowerbed i to flowerbed j at a cost of ZZ times |i-j|. Please compute the minimum total cost for Farmer John to complete his landscaping project.

    输入输出格式

    输入格式:

    * Line 1: Space-separated integers N, X, Y, and Z (0 <= X, Y, Z <= 1000).

    * Lines 2..1+N: Line i+1 contains the space-separated integers A_i and B_i.

    输出格式:

    * Line 1: A single integer giving the minimum cost for Farmer John's landscaping project.

    说明

    There are 4 flowerbeds in a row, initially with 1, 2, 3, and 4 units of dirt. Farmer John wishes to transform them so they have 4, 3, 2, and 0 units of dirt, respectively. The costs for adding, removing, and transporting dirt are 100, 200, and 1.

    One unit of dirt must be removed (from flowerbed #4), at a cost of 200. The remaining dirt can be moved at a cost of 10 (3 units from flowerbed #4 to flowerbed #1, 1 unit from flowerbed #3 to flowerbed #2).


      开玩笑的啦,怎么可能真的是英文题面啊?

    中文题面

      有n块土地,每块有A[i]泥土,现把其改造成B[i]泥土,有3种操作:

        (1)花费X向任意土地增加1泥土;

        (2)花费Y向任意土地减少1泥土;

        (3)花费Z*|i-j|把土地i的1泥土运到土地j。问最小花费是多少。


     思路分析

      首先看到对于每一块地都有三种操作,最后求取最小值,是不是第一反应就是DP啊?

      那么我们不妨来思考一个问题:

        假设转移的费用远远要小于购买或者卖掉 【相当于0.00000000000000001与100000000000000000000的区别

        此时对于一块需要加土的土地来说,

            如果能够转移的土地在很遥远的地方,远到转移比购买花费还大,怎么判断?

            如果能够转移的土地一块不够转移怎么办?

            如果转移了这一块,但是下一块要满足的话,代价会很大怎么办?

      那么显然这个时候采取动态规划就不太方便了。

      这个时候怎么办呢?

      其实对于这个问题无非就是 想买/卖,但是到后来又发现其实转移更合适一点,要怎么修改的问题。

      那么我们不妨直接了当的选取当前的最优解,同时存储下来另一个解与当前值的差值,方便之后修改。

      所以思路就显而易见了 啊。。。

        首先开两个大根堆,分别存储需要增加的土,和空余的地。

        然后对于每一个点走一次可反悔的贪心就好了。

      但是,这种做法并没有回答刚刚的所有疑问。

        如果一块不够转移怎么办?

        从另一块转移的话,那么这个大根堆不仅要维护和当前点的位置,还要维护数量............【太高级了吧,我觉得不好办

      那么我们就需要一个可以规避这种问题的方法。

        仔细想想,可以发现造成大根堆维护困难的原因不就是需要的单位面积不同嘛?

        那么我们不妨将每一块土地分割成单位面积,这样的话就没有维护数量的事情了。

        我们只要在使用之后把它pop掉就可以了。

              比如5块土地,1缺1,2缺1,3多1,4多5,5缺1

            就拆成   第一个堆 1 2 2 5        这样。

                第二个堆 3 4 4 4 4 4 

      看似没有什么问题了。。。

      等等还没完。。

        如果被转移走的那个位置忽然发现其实不是最优的怎么办???

      这个也好解决啊。

        既然刚刚已经反悔过一次了,那么我们不如就直接把反悔的部分当作一种梦想,

        相当于虚构出来一块这样的土地。

          感性理解一下,比如我这块土地x缺少1 ,土地y多余1,【只是瞎举的例子,可能不合理

                  我就直接假装把y的1给了x,但是y那里还认为自己多余1,然后在存储缺少1的堆里加上一个y

          如此就是一次梦想操作。

      基本上没什么问题了,具体代码实现细节自己体悟吧。


    代码实现

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<queue>
    using namespace std;
    
    const int inf=0x7f7f7f7f;
    int n,x,y,z;
    int ans;
    priority_queue<int> a;//囤积的泥土 
    priority_queue<int> b;//缺土的地 
    
    void JIA(int nw) {
        int cost=inf;
        if(!a.empty()) {
            if(nw*z-a.top()<x) {
                cost=nw*z-a.top();
                a.pop();
                b.push(nw*z+cost);
            } 
        }
        if(cost==inf) {
            cost=x;
            b.push(nw*z+cost);
        }
        ans+=cost;
        return;
    }
    
    void JIAN(int nw) {
        int cost=inf;
        if(!b.empty()) {
            if(nw*z-b.top()<y) {
                cost=nw*z-b.top();
                b.pop();
                a.push(nw*z+cost);
            }
        }
        if(cost==inf) {
            cost=y;
            a.push(nw*z+cost);
        }
        ans+=cost;
        return;
    }
    
    void pd(int nw,int m) {
        if(m<0) {
            JIA(nw);
            return;
        }
        JIAN(nw);
        return;
    }
    
    int main()
    {
        scanf("%d",&n);
        scanf("%d%d%d",&x,&y,&z);
        for(int i=1;i<=n;i++) {
            int c,d;
            scanf("%d%d",&c,&d);
            for(int j=1;j<=abs(c-d);j++)
                pd(i,c-d);
        }
        cout<<ans<<endl;
        return 0;
    }

    谢谢,祝AC!

  • 相关阅读:
    apple Safari input text 的兼容问题
    移动开发不能不知道的事-关于点击元素时出现的阴影
    移动开发不能不知道的事- css3复选框样式自定义
    移动开发不能不知道的事-中西文字体
    移动开发不能不知道的事-dp
    猴子吃桃
    3个数排序
    求10!
    一个简单的求和程序
    输出一个三角形
  • 原文地址:https://www.cnblogs.com/qxyzili--24/p/11190318.html
Copyright © 2011-2022 走看看