• BZOJ3791 作业(DP)


    题意:

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

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

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

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

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

    n<=100000,k<=50;


    题解

    性质:在一段区间内染色k次,每次是一段连续的,那么这个区间最多被分为2*k-1段

    证明:每次染区间中间位置,最终只会染出2*k-1段(还是不明白的自己画画图

    解题思路:

    令dp[i][j][k]表示 当前考虑到第i本作业,1-i这段区间被染成了j段,第i本作业染为k(0或1)

    则状态转移方程为

    第i本作业由两种状态转移过来:

    一、继承了i-1的状态,也就是原本有一次染色是染到i-1为止,现在把i也连着染一下,此时染色次数不变 即 dp[i-1][j][k]

    二、从i开始新一次的染色,此时i-1的状态应该和i的状态是相反的,因为如果是相同的状态的话就没必要新一次染色了,此时染色次数要+1 即dp[i-1][j-1][k^1]

    即:dp[i][j][0]=max(dp[i-1][j-1][1],dp[i-1][j][0])+(a[i]==0);
           dp[i][j][1]=max(dp[i-1][j-1][0],dp[i-1][j][1])+(a[i]==1);

     代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    int a[100010];
    int dp[100010][200][2];
    int n,K;
    int read(){
        int ans=0;char c=getchar();
        while(!(c>='0'&&c<='9')) c=getchar();
        while(c>='0'&&c<='9'){
            ans=ans*10+c-48;
            c=getchar();
        }
        return ans;
    }
    int main(){
        freopen("hwk.in","r",stdin);
        freopen("hwk.out","w",stdout);
        n=read();K=read();
        for(int i=1;i<=n;i++)
            a[i]=read();
        int ans=0,i;
        if(K==0){
            printf("0");
            return 0; 
        }
        for(int j=1;j<=2*K-1;j++){
            for(i=1;i<=n;i++){
                dp[i][j][0]=max(dp[i-1][j-1][1],dp[i-1][j][0])+(a[i]==0);
                dp[i][j][1]=max(dp[i-1][j-1][0],dp[i-1][j][1])+(a[i]==1);
                ans=max(ans,max(dp[i][j][0],dp[i][j][1]));
            }
        }
        printf("%d",ans);
        return 0;
    }
    自己选择的路,跪着也要走完
  • 相关阅读:
    【Go】windows下搭建go语言编译环境
    【java回调】同步/异步回调机制的原理和使用方法
    【tomcat】tomcat远程调试
    【tomcat】获取访问者真实IP
    【深度学习学习记录】之一:开篇闲扯一些话
    【java】线程安全的整型类AtomicInteger
    【OpenStack】源码级深入了解删除虚拟机操作
    【Maven】maven的常用命令以及搭建maven私人仓库
    素 数 (第三届省赛)
    房间安排(第三届省赛)
  • 原文地址:https://www.cnblogs.com/tonyshen/p/11372329.html
Copyright © 2020-2023  润新知