• 【线型DP】CF1012C Hills 小山坡


    来了来了。


    题目:

    给你n个数,你一次操作可以把某一个数-1(可以减为负数),你的目标是使任意的k个数严格小于它旁边的两个数(第一个数只用严格小于第二个数,第n个数只用严格小于第n-1个数),问最少需要几次操作。k是不确定的,请输出1<=k<=n/2(向上取整)时的答案。

    输入格式:

    第一行一个正整数n

    第二行n个正整数ai

    输出格式:

    一行 1<=k<=n/2 个数,第i个数代表k=i时的答案

    数据范围:

    1 ≤ n ≤ 5000

    1 ≤ ai ≤ 100000

    输入 #1
    5
    1 1 1 1 1
    
    输出 #1
    1 2 2 
    输入 #2
    3
    1 2 3
    
    输出 #2
    0 2 

    思路:

    看到这道题的第一眼,我想到了一道题,2018noipD1T1铺设道路(链接:https://www.luogu.com.cn/problem/P5019),看起来十分相似,于是这道题想用朴素的贪心写,结果果然不对,两道题的区别还是很大,贪心的正确性无法保证,所以还是回来老老实实的写动归。

    我们设定一个三维数组f,f[i][j][0]表示前i座山,修建j座房子,且第i座山没有建房子的代价,f[i][j][1]表示前i座山,修建j座房子,且第i座山建有房子的代价。

    通过简单的模拟我们可以得到,(1)f[i][j][0]会从f[i-1][j][0](直接从前一个状态搬过来,前一座山和这座山都不建房子,抹油代价)和f[i-1][j][1](前一座山已经建了房子,那么就要使这座山的高度低于前一座山的高度,代价为max(0,a[i]-a[i-1]+1),有0是因为可能这座山的高度原来就低于前一座山的高度)转移过来,转移方程为:f[i][j][0]=max(f[i-1][j][0],f[i-1][j][1]+max(0,a[i]-a[i-1]+1))   (2)f[i][j][1]的处理要比f[i][j][0]的处理相对复杂,在此我们明确一个观点,相邻的两座山不可能都建房子,并且在考虑这座山是否建房子的时候,可以只考虑这座山之前的状态,而不去考虑这座山之后的状态,这座山之后的状态我们可以在之后再处理,因此,f[i][j][1]可以从f[i-2][j-1][0]转移过来,因为第i-2座山没有建房子,所以第i-1座山的高度没有变化,代价为max(0,a[i]-a[i-1]+1)。f[i][j][1]同样可以从f[i-2][j-1][1]转移过来,因为第i-2座山已经建有房子,所以第i-1座山的高度可能会有变化,因此,总代价为:max(0,a[i-1]-(a[i-2],a[i])+1)(大家思考一下,为什么这里总代价不是:max(0,a[i-1]-min(0,a[i-1]-a[i-2]+1)-a[i]+1)) 答案(先思考再看答案):因为第i-1座山的高度一定比第i-2和i座山低,而这样做只能保证第i-1座山的高度低于第i-2座山的高度,无法保证第i-1座山的高度低于第i座山的高度,因此,我们也可以改成:max(max(0,a[i-1]-a[i]+1),max(0,a[i-1]-a[i-2]+1)) 贼长

    代码:

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<iostream>
     4 #include<cmath>
     5 #include<algorithm>
     6 using namespace std;
     7 const int maxn=5e3+5,maxe=5e3+5,INF=0x3f3f3f3f;
     8 int n,m,a[maxn],f[maxn][maxn][3],ans;
     9 inline int read(){
    10     int s=0,t=1;
    11     char ch=getchar();
    12     while(ch<'0'||ch>'9'){if(ch=='-')t=-1;ch=getchar();}
    13     while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
    14     return s*t;
    15 }//朴素快读 
    16 int main(){
    17     freopen("a.in","r",stdin);
    18     n=read();
    19     for(int i=1;i<=n;i++)a[i]=read();
    20     a[0]=INF;
    21     memset(f,0x3f,sizeof(f));
    22     f[0][0][0]=f[1][0][0]=f[1][1][1]=0;//初始化 
    23     for(int i=2;i<=n;i++){
    24         f[i][0][0]=f[i-1][0][0];
    25         for(int j=1;j<=(i+1)/2;j++){//这里我测了一下,不管是用floor+1还是用ceil都不行,神奇 
    26             f[i][j][1]=min(f[i-2][j-1][0]+max(0,a[i-1]-a[i]+1),f[i-2][j-1][1]+max(max(0,a[i-1]-a[i]+1),max(0,a[i-1]-a[i-2]+1)));
    27             f[i][j][0]=min(f[i-1][j][0],f[i-1][j][1]+max(0,a[i]-a[i-1]+1));
    28         }
    29         //cout<<floor(i/2)+1<<" "<<(i+1)/2<<endl;
    30     }
    31     for(int i=1;i<=(n+1)/2;i++)cout<<min(f[n][i][0],f[n][i][1])<<" ";
    32     return 0;
    33 }

    嘤嘤嘤,溜了

  • 相关阅读:
    HTML表格的运用
    HTML常用元素
    CSS常用样式(四)之animation
    CSS常用样式(三)
    CSS学习总结(三)
    CSS常用样式(二)
    CSS常用样式(一)
    CSS学习总结(二)
    CSS学习总结(一)
    HTML标签的嵌套
  • 原文地址:https://www.cnblogs.com/614685877--aakennes/p/12694549.html
Copyright © 2020-2023  润新知