这篇博客我要单独写!
思想:贪心的把到当前位置先补齐
假设:假设:i,j,k,p表示位置,且泥土与目标值都是相差1
i j k p
少 多 少 多
------------------------------------------------------------
i位置缺少
第0步:
花费x
------------------------------------------------------------
j位置是多出来,如果转移更优,则满足
(j - i) * z < x(把i位置及填满需要这么多钱) + y(把j位置多的去掉需要这么多)
“把j位置多的去掉的更少花费”
v = (j-i)*z - x
观察
v = (j-i) * z - x
= j*z - (x + i*z) //括号里的和什么相关:
//1.i位置填满需要的花费,2.i位置(i*z)
v累加进答案中
这里先不说完整的做法,继续推理,从下面的推导过程让“最终的方法”付出水面
------------------------------------------------------------
k位置是缺少的,如果转移更优,则满足
(k - j) * z < v(把j位置多的去掉的更少花费) + x(把k位置填满)
//把k位置填满的更少花费
w = (k - j) * z - v
= k*z - (v + j*z)//括号里的和什么相关:1.j位置去掉需要的花费,2.j位置有关(j*z)
找到规律:选择前面“多余”的位置中最大的 "花费 + 位置*z",进行交换
累加进答案中,并且 k*z + w 放入“缺少”大根堆
如果转移不优,则
(k - j) * z > v(把j位置多的去掉的更少花费) + x(把k位置填满)
则把x累加到答案中
k*z + x 放入大根堆
------------------------------------------------------------
p位置是多出来的,如果转移更优,则满足
(p - k) * z < w(把k位置填满的更少花费) + y(p位置去掉)
r = (p - k) * z - w
= p * z - (k*z + w) //括号里的和什么相关:1.k位置填满需要的花费,2.k位置有关(k*z)
找到规律:选择前面“缺少”的位置中最大的"花费 + 位置*z",进行交换
累加进答案中,并且 p*z + r 放入"多余"大根堆
代码:
#include<iostream> #include<cstdio> #include<map> #include<algorithm> #include<cstring> #include<cmath> #include<queue> #define ll long long using namespace std; priority_queue<int> qh,ql; int n,x,y,z,w,a,b; ll sum; int main(){ scanf("%d%d%d%d",&n,&x,&y,&z); for(int i = 1; i <= n; i++){ scanf("%d%d",&a,&b); while(a < b){//缺少 if(!qh.empty() && i * z - qh.top() < x){ w = i * z - qh.top(); qh.pop(); } else w = x; sum += w; ql.push(i*z + w); a++;//直到a填满 } while(a > b){//多余 if(!ql.empty() && i * z - ql.top() < y){ w = i * z - ql.top(); ql.pop(); } else w = y; sum += w; qh.push(i*z + w); a--; } } printf("%lld\n",sum); return 0; }
系列题目: 简单:https://www.luogu.com.cn/problem/P2758 编辑距离 中等:P3049 [USACO12MAR]Landscaping S dp,抓到一个条件保证 0≤Ai,Bi≤10 难:P2748 [USACO16OPEN]Landscaping P 反悔贪心