洛谷P2362 围栏木桩----dp思路

在翻dp水题的时候找到的有趣的题0v0

原文>>https://www.luogu.org/problem/show?pid=2362<<

题目描述

某农场有一个由按编号排列的n根木桩构成的首尾不相连的围栏。现要在这个围栏中选取一些木桩,按照原有的编号次序排列之后,这些木桩高度成一个升序序列。所谓的升序序列就是序列中的任何一个数都不小于它之前的任何一个数。试编写程序从这个围栏中选取合适的木桩使得选出的木桩个数t最大,并求出选取出t根木桩的方案总数c。

输入输出格式

输入格式:

文件中的第一行只有一个数m,表明随后有m个问题的描述信息。每个问题的描述信息格式为n h1,h2,h3,…,hn(其中hi(i=1,2,3,…,n)表示第i根木桩的高度。)


输出格式:

依次输出每个问题中t和c的解。每行输出一个问题的解。

这题和一般最长不下降子序列的区别就在于他需要输出最大解的种类个数.怎么统计这些可能解就是这题有意思的地方.

我们通过a数组存序列,b来记录前i个数的最长子序列,最关键的是c,c[i]的意义是记录在1~i-1中满足最大序列为b[i]-1的子序列个数.

挺难理解,我举个例子:

给8个数字 17534851 当i=3时 b[3]=2 (15或17) c[3]则表示当i=1~2中满足b[i]=1的情况 显然只有i=1时(i=2时b[2]=2) 所以c[3]=1;

i=4时 b[4]=2 c[4]=1;i=5时 b[5]=3 (134) c[5]=3 (i=2,3,4时c[i]为2).

于是乎,i=n时最长序列就在b里找,假设找到最大值bmax,而可能的情况则为 for(i=1;i<=n;i++) if(b[i]=bmax) ans+=c[i];

为什么会有这样的关系?我们在开始时先给 b c 数组赋初值1(序列最短可为自己本身),之后的讨论都是建立在a[i]>=a[r]上的.所以要在第一个循环前加上判断条件if(a[i]>=a[r]) (r的出处请脑补最长不下降子序列模板),bmax对应的c[i]意为不包括a[i]在内的最大序列为bmax-1的情况.所以如果在尾部加上a[i],最长数目就变成了bmax.这就是该算法的原理.觉得理解不充分的话可以自己举一个例子套套看,与代码结合也许会有助于理解.

#include<iostream>
using namespace std;
int main()
{
    int k,n,b[2005],c[2005],a[2005],i,j;
    cin>>k;
    while(k--)
    {
        cin>>n;
        for(i=1;i<=n;i++)
        {
            cin>>a[i];
            b[i]=c[i]=1;
        }
        for(j=2;j<=n;j++)
        {
            int r;
            for(r=j-1;r>=1;r--)
            {
                if(a[j]>=a[r])
                {
                    if(b[j]<b[r]+1)
                    {
                        b[j]=b[r]+1;
                        c[j]=c[r];
                    }
                    else if(b[j]==b[r]+1)
                    {
                        c[j]+=1;
                    }
                }
            }

        }
        int max=-1,bj=0;
        for(i=1;i<=n;i++)
        {
            if(b[i]>max)
            {
                max=b[i];
                bj=i;
            }
        }
        int ans=0;
        for(i=1;i<=n;i++)
        {
            if(b[i]==b[bj]) ans+=c[i];
        }
        cout<<b[bj]<<" "<<ans<<endl;
    }
}
 
时间: 2024-08-10 01:50:51

洛谷P2362 围栏木桩----dp思路的相关文章

洛谷P1244 青蛙过河 DP/思路

又是一道奇奇怪怪的DP(其实是思路题). 原文戳>>https://www.luogu.org/problem/show?pid=1244<< 这题的意思给的挺模糊,需要一定的人生经验理解能力. 题目想必已知,我就提几点可能会搞错的点吧. 1.题目说了青蛙可以:A→B(表示可以从A跳到B,下同),A→C,A→D,C→B,D→B,D→C,C→D:但却不能跳到A,B上再跳回来.所以不可以脑补成汉内塔那种跳法. 2.青蛙只能跳到比它大1号的青蛙上面,而不是比他大的青蛙就行.所以不可以脑补

洛谷P2018 消息传递 树形DP

洛谷P2018 消息传递 树形DP dp[ u ] 表示 u 节点 已经被传到,然后将其字节点都传到所需要的最少时间 可知一个原则 一个树中的子树中如果同时开始传,那么最晚才能传到的,那他肯定最先开始传 因为本身需要的时间就大了,如果再晚一点开始,就要更晚才能结束了 也就是说从叶子向根传,将一个树中的子树 按照 他们直接传所需要的时间排序优先传大的,再传小的 也就是一个点 只要负责好传到他们的儿子那些点就够了 1 #include <cstdio> 2 #include <cmath&g

AC日记——围栏木桩 洛谷 P2362

围栏木桩 思路: DP: 代码: #include <bits/stdc++.h> using namespace std; #define maxn 2001 int n,m,ai[maxn],dp[maxn][maxn]; int main() { scanf("%d",&n); for(int no=1;no<=n;no++) { scanf("%d",&m);int t=0,c=0; for(int i=1;i<=m;

洛谷P1108 低价购买[DP | LIS方案数]

题目描述 “低价购买”这条建议是在奶牛股票市场取得成功的一半规则.要想被认为是伟大的投资者,你必须遵循以下的问题建议:“低价购买:再低价购买”.每次你购买一支股票,你必须用低于你上次购买它的价格购买它.买的次数越多越好!你的目标是在遵循以上建议的前提下,求你最多能购买股票的次数.你将被给出一段时间内一支股票每天的出售价(2^16范围内的正整数),你可以选择在哪些天购买这支股票.每次购买都必须遵循“低价购买:再低价购买”的原则.写一个程序计算最大购买次数. 这里是某支股票的价格清单: 日期 1 2

洛谷P1162(自我感觉思路还算巧妙的一道题)

P1162 填涂颜色 题目描述 由数字0 组成的方阵中,有一任意形状闭合圈,闭合圈由数字1构成,围圈时只走上下左右4个方向.现要求把闭合圈内的所有空间都填写成2.例如:6X6的方阵(n=6),涂色前和涂色后的方阵如下: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 0 0 1 1 1 1 0 1 1 0 0 1 0 1 1 2 2 1 1 1 0 0 0 1 1 1 2 2 2 1 1 0 0 0 0 1 1 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1

洛谷P2014 选课 (树形dp)

10月1日更新.题目:在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习.现在有N门功课,每门课有个学分,每门课有一门或没有直接先修课(若课程a是课程b的先修课即只有学完了课程a,才能学习课程b).一个学生要从这些课程里选择M门课程学习,问他能获得的最大学分是多少?输入第一行有两个整数N,M用空格隔开.(1<=N<=200,1<=M<=150)接下来的N行,第I+1行包含两个整数ki和s

洛谷 P1410 子序列(DP)

这题的题解的贪心都是错误的...正解应该是个DP 考虑有哪些有关的条件:两个序列的当前长度, 两个序列的末尾数, 把这些都压进状态显然是会GG的 考虑两个长度加起来那一位的数一定是其中一个序列的末尾, 而我们要末尾的数尽量小, 所以完全可以把这个DP缩成两维 设f[i][j]为当前选到第i位, a[i]选入第一个序列, 则末尾为a[i], 第一个序列长度为j, 则第二个序列长度为i-j时第二个序列末尾的数最小为多少. 则有 if(a[i]<a[i+1]) f[i+1][j+1]=min(f[i+

洛谷P3205合唱队——区间DP

题目:https://www.luogu.org/problemnew/show/P3205 枚举点,分类为上一个区间的左端点或右端点,满足条件便+=即可: 注意不要重复(当l=2时). 代码如下: #include<iostream> #include<cstdio> using namespace std; int n,h[1005],dp[1005][1005][3],p=19650827; int main() { scanf("%d",&n);

洛谷 [SCOI2010]股票交易 | 单调性DP

题目链接 #include<cstdio> #include<algorithm> #include<cstring> #define N 2005 using namespace std; int n,q[N],ap,bp,dp[N][N],as,bs,m,w; int main() { scanf("%d%d%d",&n,&m,&w); memset(dp,128,sizeof(dp)); for (int i=1;i&l