zoukankan      html  css  js  c++  java
  • 洛谷 P1714 切蛋糕 题解

    P1714 切蛋糕

    题目描述

    今天是小Z的生日,同学们为他带来了一块蛋糕。这块蛋糕是一个长方体,被用不同色彩分成了N个相同的小块,每小块都有对应的幸运值。

    小Z作为寿星,自然希望吃到的第一块蛋糕的幸运值总和最大,但小Z最多又只能吃M小块(M≤N)的蛋糕。

    吃东西自然就不想思考了,于是小Z把这个任务扔给了学OI的你,请你帮他从这N小块中找出连续的k块蛋糕(k≤M),使得其上的幸运值最大。

    输入格式

    输入文件cake.in的第一行是两个整数N,M。分别代表共有N小块蛋糕,小Z最多只能吃M小块。

    第二行用空格隔开的N个整数,第i个整数Pi代表第i小块蛋糕的幸运值。

    输出格式

    输出文件cake.out只有一行,一个整数,为小Z能够得到的最大幸运值。

    输入输出样例

    输入 #1

    5 2
    1 2 3 4 5

    输出 #1

    9

    输入 #2

    6 3
    1 -2 3 -4 5 -6

    输出 #2

    5

    说明/提示

    对20%的数据,N≤100。

    对100%的数据,N≤500000,|Pi|≤500。 答案保证在2^31-1之内。

    【思路】

    【条件反射】

    单调队列
    看到这道题目想单调队列
    第一时间想的是单调蛋糕的幸运值

    【60分贪心解法】

    单调队列 + 贪心
    部分分解法
    贪心
    用一个ans记录目前队列里面的值
    每一次都先把队列里面超出m长度范围的数弹掉
    然后贪心
    怎么个贪心法呢?
    如果队列里面总值为正数那方就去一定比是负数更优
    所以当里面总和为负数的话就弹出
    直到为空或者为正数
    就算是0也势必负数更优的
    所以就可以一直比较最大值
    水了60分

    【部分分的局限性】

    然后发现很有问题
    因为有这种可能
    -10 5 7
    队列总和是2,正数
    如果按贪心来的话那就会直接把下一个元素放进去
    但是如果弹出队首的 -10 结果队列里面的总和会变为12
    比原来贪心的2更优
    所以用贪心这种情况是没有办法处理的

    【正解】

    既然单调单个蛋糕的幸运值是不行的
    那换个思路想一下
    这里面每个点前面连续小于等于m长度的区间最大值
    是这个点到前面一个距离小于等于m长度的点中间的值
    想一个O(1)求线性区间的方法?
    这必然是前缀和!
    因为想要这个区间最大
    那么这个点到最优点总和要最大
    可以用这个点的前缀和减去最优点的前缀和
    因为这个点的前缀和是确定的
    所以想让解法更优
    那就只能让最优点的前缀和更小
    这就会让差更大
    所以可以单调前缀和
    这样就很好搞出来咯!

    【最后结论】

    只需要单调前缀和
    和滑动窗口的处理方式一样
    deque或者数组就okoj
    如果队首的到这个点的距离大于了m那就弹出
    如果队尾的值大于将要放入的这个前缀和那就弹出
    最后这个点的值和队首的差就是这个点的最优解
    比较找最大值就好了

    【注意】

    要在弹出完比这个点的前缀和更大的点之后
    先将这个前缀和放进去
    然后在计算这个点和队尾的差
    因为有可能整个队列中的数都比这个点的前缀和大
    所以还不如不选!
    30行的代码,60行的注释,就像老奶奶讲故事

    【完整代码】

    【贪心60分】

    #include<iostream>
    #include<cstdio>
    #include<queue>
    using namespace std;
    const int Max = 500005;
    struct node
    {
    	int v;
    	int hao;
    }a[Max];
    queue<node>q;
    int main()
    {
    	freopen("acioi.in","r",stdin);
    	int n,m;
    	cin >> n >> m;
    	for(register int i = 1;i <= n;++ i)
    		cin >> a[i].v,a[i].hao = i;
    	int ans = 0;
    	int M = 0;
    	for(register int i = 1;i <= n;++ i)
    	{
    		while(!q.empty() && i - q.front().hao >= m)
    		{
    			ans -= q.front().v;
    			q.pop();
    		}
    		while(!q.empty() && ans < 0)
    		{
    			ans -= q.front().v;
    			q.pop();
    		}
    		q.push(a[i]);
    		ans += a[i].v;
    		M = max(M,ans);
    	}
    	cout << M << endl;
    	return 0;
    }
    

    【单调队列——正解】

    #include<iostream>
    #include<cstdio>
    #include<queue>
    using namespace std;
    const int Max = 500005;
    struct node
    {
    	int t;
    	int w;
    };
    deque<node> q;
    int main()
    {
    	int n,m;
    	cin >> n >> m;
    	int ans = 0;
    	int x;
    	int M = 0;
    	for(register int i = 1;i <= n;++ i)
    	{
    		while(!q.empty() &&  i - q.front().t > m)
    			q.pop_front();
    		cin >> x;
    		ans += x;
    		while(!q.empty() && q.back().w > ans)
    			q.pop_back();
    		q.push_back((node){i,ans});
    		M = max(ans - q.front().w,M);
    	}
    	cout << M << endl;
    }
    
  • 相关阅读:
    javascript framework js常用框架
    快速排序Quick sort
    归并排序
    Linux中 设置apache,mysql 开机启动
    Linux下设置mysql和tomcat开机启动
    linux命令之ifconfig详细解释
    CentOS网络接口配置文件ifcfg-eth详解
    条件测试操作与流程控制语句
    从键盘或文件中获取标准输入:read命令
    linux yum命令详解
  • 原文地址:https://www.cnblogs.com/acioi/p/11676580.html
Copyright © 2011-2022 走看看