COJ 1411: Longest Consecutive Ones (前缀和)

听说这题用双端队列做,不过某渣连双端队列都没听说过。。。囧

最后看到别人博客说用前缀和可以解决,虽然是险过,但是我觉得这个好吊的样子。

链接:

http://blog.csdn.net/yangjie_acm/article/details/24433573

 

首先,用一个pos数组记录第i个1的位置,用sum数组记录前i个1的位置的和

从第一个1的位置,连续的1的个数最大值为1开始枚举, 如果当前位置和最大值可行,更新ans= max,max++, 否则max不变 推后到下一个1的位置

判断当前位置和最大值可行的办法:

 

计算左边的1右边的1 的移动次数之和是否大于最大值K即可

例如:

100101010101 , 枚举到第2个1的位置, max= 5时, (设be= 2) 最中间的1 为 第mid= (be + be+ max -1) /2 = 4 个1, 左边1的个数为num= mid- be个

左边的移动次数 costl =  pos【4】 - pos【3】 + pos【4】 + pos【2】 -  num*(num+1) /2;

 上式中num*(num+1)/2 是因为第i个1移动的次数都要减去i

式子化简可得 costl= pos【4】*2 - sum【mid-1】 -sum【be-1】 - num*(num+1)/2;

右边1的个数numr=  max -num  -1  ;

同理可得右边的移动次数 costr= sum【be+max-1】 - sum【mid】 -  pos【4】*2  - numr*(numr+1)/2;

 

代码:

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <time.h>
using namespace std;
#define maxn 100000

long long sum[maxn+5];
int pos[maxn+5];
char ch[maxn+5];
long long k;
bool ok(int l, int r)
{
	int mid= (l + r)/2;
	int num= mid - l; //左边1的个数 ; 
	long long temp= pos[mid]* num - num*(num + 1)/2 - ( sum[mid-1] - sum[l-1] );
	num= r- mid;
	temp+= sum[r] - sum[mid] - num *pos[mid] - num *(num+1)/2;
	if(temp > k)
		return false;
	else
		return true;
}

int main()
{
	int t;
	scanf("%d",&t);
	
	while(t--)
	{
		memset(pos, 0, sizeof(pos));
		scanf("%s %lld",ch+1, &k);
		int len= strlen(ch+1);
		int count= 0; 
		sum[0]= 0;
		for(int i= 1; i<= len; i++)
		{
			if(ch[i]=='1')
			{
				count++;
				pos[count]= i;
				sum[count]= sum[count-1]+pos[count];
			}
		}
		int max= 1;
		int be= 1;
		int ans= 0;
		while(true)
		{
			if(max+ be -1>count)
				break;
			if( ok(be, be+max-1))
			{
				ans= max;
				max++;	
			}
			else		
				be++;
		}
		printf("%d\n",ans);
	}
	return 0;
}


 

你可能感兴趣的:(COJ 1411: Longest Consecutive Ones (前缀和))