给出一个长度为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; }
原文地址:http://blog.csdn.net/ww140142/article/details/47617151