zoukankan      html  css  js  c++  java
  • DP练习题——洛谷P1970花匠

    题目描述:

    洛谷(P1970)

    花匠栋栋种了一排花,每株花都有自己的高度。花儿越长越大,也越来越挤。栋栋决定把这排中的一部分花移走,将剩下的留在原地,使得剩下的花能有空间长大,同时,栋栋希望剩下的花排列得比较别致。

    具体而言,栋栋的花的高度可以看成一列整数(h_1,h_2,...,h_n)。设当一部分花被移走后,剩下的花的高度依次为(g_1,g_2,...,g_m),则栋栋希望下面两个条件中至少有一个满足:

    条件 (A):对于所有(g_{2i}>g_{2i-1},g_{2i}>g_{2i+1})

    条件 (B):对于所有(g_{2i}<g_{2i-1},g_{2i}<g_{2i+1})

    注意上面两个条件在(m=1)时同时满足,当(m > 1)时最多有一个能满足。

    请问,栋栋最多能将多少株花留在原地。

    输入输出格式:

    输入格式:

    第一行包含一个整数(n),表示开始时花的株数。

    第二行包含(n)个整数,依次为(h_1,h_2,...,h_n),表示每株花的高度。

    输出格式:

    一个整数(m),表示最多能留在原地的花的株数。

    输入输出样例:

    输入样例:

    5
    5 3 2 1 2
    

    输出样例:

    3
    

    题目分析:

    ​ 简单理解一下题意:

    大体就是说要求留在原地的花满足:序号为(2)的倍数的花是左右两盆花中最高或者是最矮的

    解法一:

    思路:

    ​ 既然是(DP)的题,那么我们先考虑贪心(其实是为了纪念(lz)):

    说句闲话:学习(DP)的最好方法是什么??

    贪心!!!

    好了好了,说正经的。

    不过还是要吐槽这道题的算法标签,竟然没人评贪心(也不知道(lz)评了没有)

    我们可以发现,这道题就是对一个有波动的函数进行简化,我们可以举两个例子,一个是样例:

    我们可以把这个图像看做一个具有两个单调区间的函数图像,我们只能去单调区间两端的点构成新的序列,也就是说我们最多能保留三盆花

    另一个是从讨论版里找来的神仙数据:

    有了上面的分析,我们可以很轻易地找出(3)个单调区间,也就是说最多有(4)盆花能够被留下

    算法:

    ​ 根据两组数据,我们可以得到这样一个算法:

    扫描所有花的高度,从而得出增减区间的个数(w),输出(w+1)

    代码实现:

    #include<cstdio>
    #include<iostream>
    
    using namespace std;
    
    inline int read()
    {
    	int F=1,num=0;
    	char c=getchar();
    	while(!isdigit(c)){if(c=='-') F=-1;	c=getchar();}
    	while(isdigit(c)){num=num*10+c-'0';	c=getchar();}
    	return num*F;
    }
    
    int h[10000010];
    int n;
    int ans=1;//考虑到函数的左区间
    int direction;
    
    int main()
    {
    	n=read();
    	for(int i=1;i<=n;++i)
    		h[i]=read();//读入 
    	if(h[2]>=h[1])	direction=1;//1表示增区间,0表示减区间 ,因为前两个是默认都选的,所以在这里加上了等号
    	for(int i=1;i<=n;++i)//我知道可以边读入边判断,但是我愿意,你管着吗?? 
    	{
    		if(i==n)
            {
    			ans++;//考虑到函数的右区间,当到了右区间时答案直接加一并且跳出循环
                break;
            }
    		if(h[i+1]>h[i])
    		{
    			if(direction==0)//如果突然进入到增区间,就标记为增区间并且答案加一
    			{
    				ans++;
    				direction=1;
    			}
    		}
    		if(h[i+1]<h[i])
            {
    			if(direction==1)//如果突然进入到减区间,就标记为减区间并且答案加一
    			{
    				ans++;
    				direction=0;
    			}
    		}
    	}
    	printf("%d",ans);//输出答案
    	return 0;
    }
    

    解法二:

    思路:

    当然是已经哭晕在厕所的(DP)

    (dp[i][0])表示在(i)处上升时的最大盆数,(dp[i][1])表示在(i)处下降时的最大盆数

    直接得到状态转移方程

    (if(a[i]>a[i-1])spacespacespace dp[i][0]=dp[i-1][1]+1;\if(a[i]<a[i-1])spacespacespace dp[i][1]=dp[i-1][0]+1;\dp[i][0]=max(dp[i][0],dp[i-1][0]);\dp[i][1]=max(dp[i][1],dp[i-1][1]);)

    解释一下:

    (a[i])是上升的时候,将上升时的最大盆数更新为上一盆下降时的最大盆数+(1),并且用这两种状态的最大值来维护(dp[i][0]),表示满足条件(B)

    (a[i])是下降的时候,将下降时的最大盆数更新为上一盆上升时的最大盆数+(1),并且用这两种状态的最小值来维护(dp[i][1]),表示满足条件(A)

    ​ 如果还不能理解就画个图,手推一下233~~

    算法:

    ​ 就是将(dp[1][1])(dp[1][0])都更新为(1),然后从(2)开始跑一遍循环,(CV)上面的方程就结束了

    Talk is cheap,show me the code:

    #include<cstdio>
    #include<iostream>
    
    using namespace std;
    
    const int maxn=101000;
    int dp[maxn][2],a[maxn];
    int n;
    
    inline int read()
    {
    	int F=1,num=0;
    	char c=getchar();
    	while(!isdigit(c)){if(c=='-')	F=-1;	c=getchar();}
    	while(isdigit(c)){num=num*10+c-'0';		c=getchar();}
    	return F*num;
    }
    
    int main()
    {
        n=read();
        dp[1][0]=1;dp[1][1]=1;
        for(int i=1;i<=n;i++) a[i]=read();
    	for(int i=2;i<=n;i++)
    	{
            if(a[i]>a[i-1]) dp[i][0]=dp[i-1][1]+1;
            if(a[i]<a[i-1]) dp[i][1]=dp[i-1][0]+1;
            dp[i][0]=max(dp[i][0],dp[i-1][0]);
            dp[i][1]=max(dp[i][1],dp[i-1][1]);
        }
        printf("%d
    ",max(dp[n][1],dp[n][0]));
        return 0;
    }
    

    结语:

    贪心大法真的好诶(逃

  • 相关阅读:
    spring事物配置,声明式事务管理和基于@Transactional注解的使用
    spring集成ehcache本地缓存
    Java并发编程:volatile关键字解析
    Callable接口、Runable接口、Future接口
    Sorting It All Out
    Borg Maze
    Agri-Net
    Highways
    Truck History
    Arbitrage
  • 原文地址:https://www.cnblogs.com/juruohqk/p/11130392.html
Copyright © 2011-2022 走看看