HDU 4193(单调队列)

HDU 4193(单调队列)

问题描述
You are given a sequence of n numbers a0,…, an-1. A cyclic shift by k positions (0<=k<=n-1) results in the following sequence: ak ak+1,…, an-1, a0, a1,…, ak-1. How many of the n cyclic shifts satisfy the condition that the sum of the fi rst i numbers is greater than or equal to zero for all i with 1<=i<=n?

Input
Each test case consists of two lines. The fi rst contains the number n (1<=n<=106), the number of integers in the sequence. The second contains n integers a0,…, an-1 (-1000<=ai<=1000) representing the sequence of numbers. The input will finish with a line containing 0.

Output
For each test case, print one line with the number of cyclic shifts of the given sequence which satisfy the condition stated above.

Sample Input
3
2 2 1
3
-1 1 1
1
-1
0

Sample Output
3
2
0

题意:
一个长度为n的循环序列,每次可以将序列移动k位得到一个新的数字。显然一共可以得到n种不同的数组。然后问:一共有多少种序列,满足前i项和都大于等于0(0<=i<=n)

解题思路
1.对于循环序列,常用的方法都是数组,将原序列再复制一份到序列的后面,形成一个长度为2n的序列。
2.对数组中的值求和,sum[i]表示数组前i项的和。那么sum[i]-sum[k-1]表示序列中第k个数到第i个数的和。也可以理解为:以k为起点的序列的前j项和,j=i-k+1.
3.如果在区间值,最小的sum[i]-sum[k-1]>=0的话,那么在这个区间中的所有值也一定大于等于0。那么问题就可以转换成在一个滚动区间中求最小值。因此我们只需要维护一个队列第一个元素是最小值的单调队列。

代码

#include <iostream>
#include<queue>
#include <algorithm>
#include <string.h>

using namespace std;
int n;
int a[1111111];
int sum[2111111];
int head, rear;

```cpp
int main()
{
    while (~scanf("%d", &n) && n)
    {
        int ans = 0;
        sum[0]=0;
	for (int i = 1; i <= n; i++)   //数组复制
        {
            scanf("%d", &a[i]);
            //将输入的数组保存在数组a[]
            sum[i]=sum[i-1]+a[i];
        }
        for(int i=n+1;i<2*n;i++)
        {
            sum[i]=sum[i-1]+a[i-n]; //将循环求和变成普通序列求和
        }
       /* for(int i=0;i<2*n;i++)
        {
           cout<<i<<" "<<sum[i]<<endl;
        }*/
        deque<int>Q;
        Q.clear();
        for(int i=1;i<2*n;i++)        //核心代码部分
        {
           while(!Q.empty() &&sum[i]<=sum[Q.back()]) Q.pop_back(); //保持队列的单调性
           while(!Q.empty() &&Q.front()<i-n) Q.pop_front(); //超过k的长度则消除队列前面的元素
           Q.push_back(i);
           if(sum[Q.front()]-sum[i-n]>=0 && i>=n)        
           {
               ans+=1;   //sum[n]-sum[m]所得出的是n-1到m+1之间的和
           }
        }           
         printf("%d\n",ans);
      }
      return 0;
 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值