• [SCOI2014]方伯伯的玉米田 //二维树状数组优化DP


    题目:

    方伯伯在自己的农田边散步,他突然发现田里的一排玉米非常的不美。这排玉米一共有N株,它们的高度参差不齐。方伯伯认为单调不下降序列很美,所以他决定先把一些玉米拔高,再把破坏美感的玉米拔除掉,使得剩下的玉米的高度构成一个单调不下降序列。方伯伯可以选择一个区间,把这个区间的玉米全部拔高1单位高度,他可以进行最多K次这样的操作。拔玉米则可以随意选择一个集合的玉米拔掉。问能最多剩多少株玉米,来构成一排美丽的玉米

    n<=10000 , k <= 500


    我第一眼想差分,但是发现不可做,因为我不会在差分上跑最长不降子序列╮( ̄⊿ ̄")╭

    仔细思考一会之后看了题解好像有个好性质:

    对于最优解,拔高区间右界必须为n

    其实很显然(那你为什么看题解),如果拔高时不带上右边的玉米,显然对右边的玉米能否加入序列不利,而拔高时带上右边的玉米是“免费的”,

    发现这个性质后,这个DP突然变得很简单,我挑出来一个序列,序列中“降”的值不超过k,就像这样

     

     没错 这图是我画的 不然怎么能这么丑

    突然很清晰了有没有?

    这里我们可以自己可以口胡出来方程,dp[以第i个为结尾的序列][用了k次拔高] = 这个序列的长度

    转移为dp[i][k] = max(dp[j][k-q]+1,dp[i][k])其中q为需要拔高多少(如果不用则为0),j为i前的玉米

    欢欢喜喜写完过样例一交10pts,剩下全 泰拉E TLE,啥啊?

    仔细一想O(10000 * 10000 * 500) = O(500,0000,0000)五百亿.....

    孩子只做过矩阵加速DP,但这题不能矩阵啊.....难过的去看了题解,真就二维树状数组啊.....

    赶紧去学习了一下这是个什么高级东西,好像也不太难,就是在二维上干跟一维一样的事,

    可以看出我们要快速取出dp[j][k-q]中的最大值,考虑用二维树状数组取出最大值

    我们尝试改变dp数组的含义

    dp[第i个拔高后的高度j][拔了k次],这样相对爽,因为这样可以直接拽出来高度对应的答案

    直接把dp改为二维树状数组,dp[至多到第i个拔高后的高度j][至多拔了k次],发现爆了空间

    用01背包思想去掉第一维并在dp时倒序

     1 #include<bits/stdc++.h>
     2 
     3 using namespace std ;
     4 
     5 int a[10010],n,m,upl;
     6 int dp[5510][510];
     7 
     8 int lowbit(int x){
     9     return x&-x;
    10 }
    11 
    12 void add(int x,int y,int a){
    13     for(int i=x;i<=upl;i+=lowbit(i)){
    14         for(int j=y;j<=m+1;j+=lowbit(j)){
    15             dp[i][j] = max(dp[i][j],a);
    16         }
    17     }
    18 }
    19 
    20 int ask(int x,int y){
    21     int ret = 0;
    22     for(int i=x;i>=1;i-=lowbit(i)){
    23         for(int j=y;j>=1;j-=lowbit(j)){
    24             ret = max(dp[i][j],ret);
    25         }
    26     }
    27     return ret;
    28 }
    29 
    30 int main(){
    31     ios::sync_with_stdio(false);
    32     cin>>n>>m;
    33     for(int i=1;i<=n;i++){
    34         cin>>a[i];
    35         upl = max(upl,a[i]);
    36     }
    37     upl += m;
    38     int ans = 0;
    39     for(int i=1;i<=n;i++){
    40         for(int k=m;k>=0;k--){
    41             int x = ask(a[i]+k,k + 1) + 1;
    42             ans = max(x,ans);
    43             add(a[i]+k,k+1,x);
    44         }
    45     }
    46     cout<<ans<<endl;
    47     return 0;
    48 }
  • 相关阅读:
    [转]PHP学习入门的一些基础知识
    原来我一直徘徊在选择中
    do while循环学习
    C#装箱
    我的新手学习失败之谈
    C#教程第五课:方法
    转.iPhone开发网站、论坛、博客
    数据库作业Email发送
    安装卸载WINDOWS服务
    SQL SERVER 2008 数据库收缩语句
  • 原文地址:https://www.cnblogs.com/SINXIII/p/11269275.html
Copyright © 2020-2023  润新知