博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
toj 4074 CF 319C 斜率优化dp
阅读量:5337 次
发布时间:2019-06-15

本文共 2486 字,大约阅读时间需要 8 分钟。

Source:

 

题意:

有n棵树(n < 10^5),每棵高度为a[i],还有个属性b[i]。现在我们有个锯子来砍树,每用一次就需要维修,维修的费用是b[i],i = max(已经砍完的树的id),如果没有砍完的树,则不能维修。保证a[i]严格递增,b[i]严格递减,且a[1] = 1, b[n] = 0。求砍完所有树的最小代价。

分析:

注意到题目的条件,意味着第一次必砍第一棵树。又注意到最后一棵树的b[i] = 0,意味着我们砍了最后一棵树,所有树都砍完了。这样题目的要求就转换为最小的代价砍掉最后一棵树。

显然我们砍一棵树就砍到底,且砍的树的id一定是递增的。得到dp方程 dp[i] = min(dp[j] + (a[i]-1) * b[j]) + b[i],dp[1] = b[1],答案为dp[n]。可以在O(n^2)内解决问题。

实际上也就等价于dp[i] = min(dp[j] + a[i] * b[j]),dp[1] = 0,我们就这个方程进行优化。

考虑 j < k < i,现在dp[i]要选择j或k,假设k比j优,即

dp[j] + a[i] * b[j] > dp[k] + a[i] * b[k]

dp[j] - dp[k] > (b[k] - b[j]) * a[i]

(dp[j] - dp[k]) / (b[k] - b[j]) < a[i]

(dp[j] - dp[k]) / (b[j] - b[k]) > -a[i]

也就是说,对任意p点,取x = b[p], y = dp[p], 则有:如果斜率K(jk) > -a[i],那么k比j优。(反之j比k优,若相等则一样优)

由于这题a[i]是单调增的,也就是-a[i]是单调减的,所以一旦有K(jk) > -a[i],之后k永远比j优,所以j可以直接舍弃了。

其实也就是最初始的那个式子,斜率K = b[p], 截距B = dp[p],然后一系列直线,a[i]一直向右推移,决策肯定也一直是单调的,我就只考虑到这里,wa到不能自拔,一直认为是爆longlong了=。=(现在我也不懂这种考虑错哪了。。还求指教)

(update:下午发完解题报告晚上就想到了。。有可能出现j<k<i<p,而p决策时j优于k却劣于i的情况。。导致没取i而取了j,而且以后一直取的j=。=。。)

学习了一下斜率优化,还没太理解,多了一步:

考虑 j < k < i < p,现在要对p进行决策,

如果 K(jk) < K(ki),则k是不可能作为最优决策的。

K(ki) = -a[p]就不说了。如果K(ki) > -a[p],则i比k优;如果K(ki) < -a[p],则K(jk) < -a[p],也就是上面讨论过的情况的反面,也就是j比k优。也就是说k不可能作为最优决策。

因此可能是最优决策的序列一定是斜率单调下降的,也就是上凸性。

然后就是用单调队列来维护这个决策序列了。

 

1 #include
2 #include
3 using namespace std; 4 5 const int maxn = 1e5 + 100; 6 int n; 7 long long a[maxn], b[maxn], f[maxn], q[maxn]; 8 9 int main()10 {11 while(scanf("%d", &n) != EOF){12 for (int i = 1; i <= n; i++) scanf("%lld", a+i);13 for (int i = 1; i <= n; i++) scanf("%lld", b+i);14 int head, tail;15 head = tail = 0; q[tail++] = 1;16 f[1] = 0;17 for (int i = 2; i <= n; i++){18 while(head + 1 < tail){19 int j = q[head], k = q[head+1];20 //if (f[j] + b[j] * a[i] >= f[k] + b[k] * a[i])21 if ((f[j] - f[k]) >= (b[k] - b[j]) * a[i]) head ++; //K(jk) > -a[i],k更优,踢j22 else break;23 }24 f[i] = f[q[head]] + b[q[head]] * a[i];25 while(head + 1 < tail){26 int j = q[tail-2], k = q[tail-1];27 if ((double)(f[j]-f[k])/(b[j]-b[k]) < (double)(f[k]-f[i])/(b[k]-b[i])) tail --;  //维护上凸性(开始一直wa,是因为没有这个维护过程)28 else break; //听说相乘爆longlong29 }30 q[tail++] = i;31 }32 printf("%lld\n", f[n]);33 }34 return 0;35 }

 

转载于:https://www.cnblogs.com/james47/p/4253627.html

你可能感兴趣的文章
母版页中引用图片,外部js、css文件的路径问题 [转]
查看>>
微信小程序_(组件)组件基础
查看>>
原生Js_制作简易日历
查看>>
2015年10月14日学习笔记
查看>>
hdu 1026 Ignatius and the Princess I
查看>>
数据结构考研模糊知识点2.1
查看>>
【每日算法】交换排序算法之冒泡排序
查看>>
Java-NIO(四):通道(Channel)的原理与获取
查看>>
pandas 的算术运算和数据对齐
查看>>
R语言-增加图例
查看>>
linux 建立桌面快捷方式,让所有用户都能在桌面直接打开某软件
查看>>
CF1039E Summer Oenothera Exhibition 根号分治,LCT,ST表
查看>>
Linux安装JDK
查看>>
iptables原理详解(一)
查看>>
mySQL遇到的问题
查看>>
线性判别分析(线性回归、对数几率回归、线性判别分析和广义线性判别分析)...
查看>>
集群架构基础必会
查看>>
实现TCP连接的AT指令
查看>>
jquery 备忘笔记
查看>>
Ubuntu下安装eclipse
查看>>