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!