码迷,mamicode.com
首页 > 其他好文 > 详细

bzoj-3791 作业

时间:2015-08-13 20:12:32      阅读:167      评论:0      收藏:0      [点我收藏+]

标签:bzoj   贪心   动态规划   

题意:

给出一个长度为n的01序列;

你可以进行K次操作,操作有两种:

1.将一个区间的所有1作业写对,并且将0作业写错;

2.将一个区间的所有0作业写对,并且将1作业写错;

求K次操作后最多写对了多少作业;

n<=100000,k<=50;


题解:
考虑每次操作的影响,显然第一次操作区间越大越好,那就将整个区间覆盖了;

然后所有的01都有了一个1或者-1的权值;

贪心做的话,每次操作就想让这个权值和越大越好;

而这正好是对的;

正确性可以考虑一些反例,显然如果存在另外的操作序列最优;

这样的策略也有同样的最优解存在;

那么就是直接搞了;

每次操作跑一DP遍,找最大值的区间,然后更新区间更新答案;

注意第一次操作要单独弄出来搞,还要分两种情况讨论;

时间复杂度O(nk),似乎比纯DP快一些;

具体可以见代码;


代码:


#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 140142
int a[N],f[N];
bool t[N];
int main()
{
	int n,m,C,i,j,k,tim,x,y,l,r,sum,min_sum,now,ans;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++)
	{
		scanf("%d",t+i);
	}
	for(C=0,ans=0;C<=1;C++)
	{
		for(i=1,now=0;i<=n;i++)
		{
			if(t[i]^C)
				a[i]=1;
			else
				a[i]=-1,now++;
		}
		for(tim=2;tim<=m;tim++)
		{
			memset(f,0,sizeof(f));
			for(i=1,sum=min_sum=k=0;i<=n;i++)
			{
				sum+=a[i];
				min_sum=std::min(min_sum,sum);
				f[i]=sum-min_sum;
				if(f[i]>k)
					k=f[i],j=i;
			}
			if(!k)	break;
			for(i=j,sum=0;i>0;i--)
			{
				sum+=a[i];
				a[i]=a[i]==1?-1:1;
				if(sum==f[j])
					break;
			}
			now+=f[j];
		}
		ans=std::max(now,ans);
	}
	printf("%d\n",ans);
	return 0;
}



bzoj-3791 作业

标签:bzoj   贪心   动态规划   

原文地址:http://blog.csdn.net/ww140142/article/details/47617151

(1)
(1)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!