poj2018(斜率数形结合)

来源:互联网 发布:java 6.0 64位 编辑:程序博客网 时间:2024/05/16 00:49

题意:给定一个长度为N的数组,求其中长度大于等于K的连续的一段,使其算数平均值最大。输出最大平均值*1000取整的结果。

斜率斜率斜率。。大致感觉可以用斜率来做。。我试了试把最优决策的选择化成斜率的式子,但发现这本来就是斜率的式子,于是就不知道怎么弄了。。何老师讲的类似分数规划的方法来做。。但是我觉得斜率也应该可行,就上黑板去楚了三个点点,大致说清楚了上凸包的中间的一个点对于最后的答案不会有任何贡献。。然后何老师就让我下来写一下。。

正儿八经的题解:处理成前缀和,然后将下标作为x轴,前缀和作为y轴,两个下标之间的平均值就对应了这两个点之间的斜率,求最大的斜率即可。在草稿纸上画三个形成上凸包的点,两两之间连线,把平面分成三个区域,每个区域中的点的最大斜率的决策点要么是这三个点中最左边的,要么是最右边的,也就是说在上凸包上的点永远不可能有贡献。

那么按照斜率优化的做法,单调队列维护一下就行了。单调队列保存好的就是一个斜率递增的曲线,找到这个曲线和新加入的点的切线斜率即是最优值。每相邻两个点之间的斜率减去到新加入的点的斜率的值是递增的,最接近零的就是切线了,所以可以二分查找。实际过程中发现这个切点永远会比上一次的切点靠右,于是对对首进行单调操作即可均摊O(1)实现。

上学期和暑假做了一些斜率优化的题,都是按照模式进行的,没怎么真正理解。这次碰到斜率优化的鼻祖题,感觉那些比较复杂的操作模式反而不好针对这个太原始的题了,于是就用最原始的办法来推了一下。。于是我大致想明白为什么以前那些斜率优化可以直接毫不顾忌地删对首删队尾了。。

 

 

#include<cstdio>#include<cstring>#include<cstdlib>#include<cmath>#include<algorithm>using namespace std;int n,f;int sum[100005];int q[100005],l,r;double xielv(int i,int j){return double(sum[j]-sum[i])/double(j-i);}int main(){scanf("%d%d",&n,&f);int x;for (int i=1;i<=n;i++) {scanf("%d",&x);sum[i]=sum[i-1]+x;}double ans=0;l=1,r=0;q[++r]=0;//注意斜率优化0这个许多情况一块加进去for (int i=f;i<=n;i++){while (l+1<=r&& xielv(q[l],i)<xielv(q[l+1],i)) l++;ans=max(ans,xielv(q[l],i));int k=i-f+1;while (l+1<=r && xielv(q[r-1],q[r])>xielv(q[r],k)) r--;q[++r]=k;}ans=ans*1000;int t=ans;printf("%d",t);return 0;}


总结

1:这题想到用斜率试一下了,但是常规方法推不出来,实际上发现他本身就是一个斜率的表示方法,然后分析得知,其满足下凸包的性质,并且决策也有单调性(这个证明没有学会),好题,便于对斜率的数形结合方法的理解。

2:

0 0
原创粉丝点击