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

AT3575 101 to 010(DP)

时间:2019-10-16 09:33:23      阅读:60      评论:0      收藏:0      [点我收藏+]

标签:std   ret   amp   name   scan   clu   位置   cst   char   

满分做法:

这道题蛮难想的,\(dp[i]\)表示到i位置的最大操作数,\(11111101\)\(1011111\)这两种情况我们可求出它的最大操作数为长度\(-2\),并发现整个序列的操作数可以拆分成几个小序列的最大操作做次数之和。

所以我们要记录\(l[i]\)表示左边离\(i\)最近的\(0\)的位置,并得出转移:

\(dp[i]=max(dp[i],dp[l[i-2]]+i-l[i-2]-2)\),\(dp[i]=max(dp[i],dp[l[i-2]+1]+i-l[i-2]-1-2)\) (s[i-1]==‘0‘&&s[i-2]==‘1‘)。为什么会是两个呢?

因为这个序列可能长这样:\(111111011111101\),这样对于第一个转移来说第二个明显更优,而对于\(001111101\)这样的序列,第一个更优.

\(dp[i]=max(dp[i],dp[l[i]-2]+i-l[i]+2-2)\) (l[i]>1&&s[l[i]-1]==‘1‘),剩下的直接赋值即可。

#include<cstring>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const int maxm=500007;
char s[maxm];
int n;
int dp[maxm];//到i位置的最大操作数 
int l[maxm];//在左边离i最近的0的位置 
int main()
{
 scanf("%d",&n);
 scanf("%s",s+1);
 for(int i=1;i<=n;i++)
 {
  if(s[i]=='1') l[i]=l[i-1];
  else l[i]=i;  
 }
 for(int i=1;i<=n;i++)
 {
   dp[i]=dp[i-1];
   if(s[i]=='1')
   {
     if(s[i-1]=='0'&&s[i-2]=='1')
     {
       dp[i]=max(dp[i],dp[l[i-2]]+i-l[i-2]-2);//100|11111101的贡献为他们的长度-2,
       dp[i]=max(dp[i],dp[l[i-2]+1]+i-l[i-2]-1-2);//11111101|11111101
     }
     else if(l[i]>1&&s[l[i]-1]=='1')
     {
      dp[i]=max(dp[i],dp[l[i]-2]+i-l[i]+2-2);//101111111
     }
   }
 }
 printf("%d\n",dp[n]);
 return 0;  
}

AT3575 101 to 010(DP)

标签:std   ret   amp   name   scan   clu   位置   cst   char   

原文地址:https://www.cnblogs.com/lihan123/p/11683224.html

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