2016 UESTC Training for Dynamic Programming J - 柱爷抢银行II dp单调队列优化

J - 柱爷抢银行II

Time Limit: 1000/1000MS (Java/Others)     Memory Limit: 120000/120000KB (Java/Others)
Submit  Status

若干年后,柱爷再次来到了喵蛤蛤城,准备再干一票!!

这一次,喵蛤蛤城早已不是之前的样子,而是一个由 N 个格子组成的环,格子编号为 1..N ,按顺时针排列。第 1 个格子和第 N 个格子是相

邻的。

每个格子都有一家银行,抢第 i i个银行的收益是 ai 。柱爷也可能遇上陷阱,所以收益可以是负数。

柱爷决定从这些银行中选择一个区间,沿顺时针抢劫这个区间内的所有银行!但为了不被抓到,柱爷最多只能抢 K 个银行。

当然,柱爷既然下定决心了,就至少得抢一家银行。

请你帮柱爷选一个区间,让柱爷抢到最多的钱。

Input

第一行两个整数 N K

第二行有 N 个整数 ai ,表示抢劫第 i  家银行的收益。

数据保证:

  • 1KN1000000                                           

  • 1000ai1000                                  

Output

输出一行三个数 xlr 用空格隔开。

分别表示柱爷最多能抢到的钱,最开始的银行编号,最后的银行编号。如果答案不唯一,输出开始编号最小的。如果还不唯一,输出区间长度

最短的。

Sample input and output

Sample Input Sample Output
6 3
6 -1 2 -6 5 -5
7 1 3
6 6
-1 -1 -1 -1 -1 -1
-1 1 1


Source

2016 UESTC Training for Dynamic Programming


My Solution

dp单调队列优化

dp[i] = sum[i] - sum[que.front()];  , i-k<=j<i

用deque(自己写了个双端队列写挂了(┬_┬),应该收藏个版)
来记录队列中元素的下标, 队首为当前最小值

1.如果队首元素的位置距离当前位置 > k,队首出队

2.dp[i]=sum[i] - sum[que.front()]   
         
3.所有大于sum[i]的队尾元素出队,sum[i]入队

所有前缀和只出入队一次,所以时间复杂度为O(N)


#include <iostream>
#include <cstdio>
#include <deque>
using namespace std;
const int maxn = 1000000 + 8;
int val[maxn], sum[2*maxn], dp[2*maxn];//que[maxn], head = 0, qend = 0; !!!!!写不好, 用双端队列算啦

deque<int> que;

//内存够用que就不节约利用了
//最开始想用 two pointers 来做,但失败了
//!单调队列优化
int main()
{
    #ifdef LOCAL
    freopen("a.txt", "r", stdin);
    #endif // LOCAL
    int n, k, L = 0, R = 0;
    scanf("%d%d", &n, &k);

    for(int i = 0; i < n; i++){
        scanf("%d", &val[i]);
        if(i!=0) sum[i] = sum[i-1] + val[i];
        else sum[i] = val[i];
    }
    for(int i = n; i < 2*n-1; i++) sum[i] = sum[i-1] + val[i-n];

    dp[0] = val[0]; que.push_back(0);
    for(int i = 1; i < 2*n-1; i++){
        while(!que.empty() && i - que.front() > k) que.pop_front();

        if(dp[i-1] < sum[i] - sum[que.front()]){
            dp[i] = sum[i] - sum[que.front()];
            L = que.front() + 1;
            R = i;
        }
        else dp[i] = dp[i-1];

        //维护单调队列
        while(!que.empty() && sum[i] < sum[que.back()]) que.pop_back();



        if(!que.empty() && sum[i] >= sum[que.back()]) que.push_back(i);  //!!!!!!
        else if(que.empty()) que.push_back(i);  //!!!!!! 前面漏了这里,因为可能pop完了,然后要push进去呀
        //cout<<"here"<<endl;
        /*
        for(auto j = que.begin(); j != que.end(); j++){
            cout<<sum[*j]<<" ";
        }
        printf("\n");
        */
    }
    printf("%d %d %d", dp[2*n-2], (L % n) +1, (R%n)+1);

    return 0;
}

Thank you!
                                                                                                                                               ------from  ProLights

你可能感兴趣的:(dp,ACM,for,Training,dp单调队列优化)