题目链接:http://codeforces.com/problemset/problem/319/C
以前不知道斜率DP优化,遇到这题,学会了,要加深印象:
题意大致是砍树,不一定按顺序看,目的是为了使充电费用最低,看完标号最大的树,充电就不再需要费用,所以关键在于求砍完标号最大的树所需要的最小花费,DP优化,记dp[i]为砍倒第i棵树所需要的最小花费,所以所以对于dp[n], dp[n] = min{ dp[j] + b[j] * a[n]}, j = 1, 2, 3, ...... n - 1。注意这里题目条件单调的,这可以作为斜率DP的一个标志。
分析如下:设现在要求dp[k], 在k之前的任意两个数,我们设为i, j, 且i < j < k; 那么dp[j] + b[j] * a[k] 和 dp[i] + b[i] * a[k]两个方案那个更优呢?作差比较, 移项化简得:如果 (dp[j] - dp[i]) / (bi - bj) < ak 则 方案 j 优于方案 i。(这里要注意吧bi递减,移项变号),我们把左边看作一个两点的斜率,得到一个函数G(j, i)如果G(j, i)< ak 则 j 优于 i。可以删除 i 方案。且以后 j 之后再有其他数,i 也不可能成为最优方案,因为 j 比 i 优。另外 当G(j, i) < G ( k , j )时, 可以删除 j 方案,因为这样 j 永远不会是最优方案, 以上两步优化维护了一个单调队列, 图形上看相邻两点的斜率单调递增或递减,所以叫斜率DP优化吧?原来复杂度n*n的降为了O(n),因为每个方案之进入队列一次,被提出以后不再加入。
代码如下:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<algorithm>
#include<stack>
#include<deque>
#include<set>
#include<vector>
#include<iomanip>
#include<cctype>
#include<string>
#include<memory>
#include<map>
#include<sstream>
#include<cassert>
#define mem(a) memset(a, 0, sizeof(a))
typedef long long LL;
typedef double dou;
const int Mod = 1000000007;
const double eps = 1e-8;
const LL inf = 1e18;
const int inf1 = 1e9;
using namespace std;
const int N = 100005;
LL dp[N], qu[N], a[N], b[N];
int main()
{
LL n, i, j, st, en, s = 1;
cin >> n;
for (i = 1; i <= n; ++i)
cin >> a[i];
for (i = 1; i <= n; ++i)
cin >> b[i];
st = 1;
en = 2;
qu[1] = 1;
dp[1] = 0;
for (i = 2; i <= n; ++i)
{
while (st < en - 1 && (dp[qu[st + 1]] - dp[qu[st]]) < (b[qu[st]] - b[qu[st + 1]]) * a[i])
st++;
dp[i] = dp[qu[st]] + b[qu[st]] * a[i];
while (st < en - 1 && en > 2 && (dou) (dp[qu[en - 1]] - dp[qu[en - 2]]) / (b[qu[en - 2]] - b[qu[en - 1]]) >= (dou) (dp[i] - dp[qu[en - 1]]) / (b[qu[en - 1]] - b[i]))
en--;
qu[en++] = i;
}
cout << dp[n] << endl;
return 0;
}
参考资料:
http://www.cnblogs.com/ka200812/archive/2012/08/03/2621345.html
http://hi.baidu.com/prowindy/item/aa9ae4e285a6a4295b7cfb81?qq-pf-to=pcqq.c2c
推荐题目:http://acm.hdu.edu.cn/showproblem.php?pid=3507