题目网址点击打开链接
题目大意给你n堆牌,每一堆牌有对应的牌数ai以及惩罚值bi,然后就做个游戏,从第一堆开始拿,把ai张牌都摊开正面朝上,然后要求再把bi张牌翻转,如果你此时手头的正面朝上的牌的数量>=此时需要翻转的牌的数量,就继续拿下一堆,直到n堆牌都取完了或者是取到某堆牌时,此时手头的正面朝上的牌的数量<此时需要翻转的牌的数量,那么游戏结束,你可以拿到第一堆到目前这一堆的所有牌。然后又说你可以把第一堆的牌放到最后面,问最少要进行多少次这样的操作,可以取得最多的牌。
做法就是把a数组和b数组都倍增,a[i+n]=a[i](0<=i<=n-1),这样的话执行一次移动操作,我们不用真的移动,我们只需要把长度为n的区间往右边移动一个位子就可以了,比如说移动一次,这n个数的第一个数就变成a[1]了,然后从a[1]遍历到a[n].因为求的是最少的操作次数,所以操作次数从0到n-1.最多次数是n-1,是因为次数大于n-1的话,得到的序列就开始重复了。操作次数是多少,区间起点位置就是多少,用sum记录从区间起点到目前获取的牌总数,num来记录从区间起点到现在这堆牌的suma-sumb的差,如果num<0了表示游戏结束,更新一下信息,记录下目前这堆牌的位置,然后下次遍历的区间的起点就是这堆牌的位置+1了。然后还要特判一下,游戏结束的时候有没有把n堆牌取完,有的话就打印此时的操作次数。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
int main()
{
long long a[2*maxn],b[2*maxn],n,tempans,ans,sum,num,j,lastend,maxx=-1;
while(scanf("%d",&n)!=EOF)
{
for(int i=0;i<n;i++)
{
scanf("%lld",&a[i]);
a[i+n]=a[i];
}
for(int i=0;i<n;i++)
{
scanf("%lld",&b[i]);
b[i+n]=b[i];
}
for(tempans=0;tempans<n;tempans=lastend+1)
{
sum=0;
num=0;
for(j=tempans;j<(tempans+n);j++)
{
sum+=a[j];
num+=(a[j]-b[j]);
if(num<0)
{
lastend=j;
if(sum>maxx)
{
maxx=sum;
ans=tempans;;
}
break;
}
}
if(j==(tempans+n))
{
ans=tempans;
break;
}
}
printf("%lld
",ans);
}
return 0;
}