链接
https://www.luogu.org/problem/show?pid=1020
题解
第一问,直接DP。
关于第二问:就是用最少的链串起所有的点。
考虑新加入一个元素,假设前面的已经用最少的链串起来了。那么当前点该加到哪一条链上?
如果我们记录了前面每一个点的next(即所在链的下一个点)。如果next为空,且这个点的权值≥新加入点的权值,就可以直接把新加入的点串到后面,这肯定是最优的选择。
但如果扫描了前面所有的点,都没找到上述情况的话,我可以新开辟一条链,并以当前点为首点;还有一种选择,把前面一条链断开,把当前点接上去,这样的话链数同样增加了1。不难发现,这两种决策是等价的。因为后面再来点的时候,接到这两条链的末端造成的情形是一样的,在这两条链的某处断开,造成的情形也是一样的。所以说这两种操作没有什么区别...,因此我们就可以不考虑断链的决策,只是在后面添加或者开辟新链,这样这道题就很好写了。记录链数和每条链末端的权值,每次来一个点就扫描所有的末端,如果找到合适的末端就把那个末端的权值改成当前点的权值,否则新建一个末端,权值等于当前点权值。
时间复杂度O(N^2)(其实可以优化但没必要)
代码
//dp
#include <cstdio>
#include <algorithm>
#define maxn 1000
using namespace std;
int h[maxn], f[maxn], N, ans, list[maxn];
int main()
{
int i, j;
for(N=1;~scanf("%d",h+N);N++)
{
f[N]=1;
for(i=1;i<N;i++)
{
if(h[N]<=h[i])f[N]=max(f[i]+1,f[N]);
}
ans=max(ans,f[N]);
}
printf("%d\n",ans);
N--;
for(ans=0,i=1;i<=N;i++)
{
for(j=1;j<=list[0];j++)
{
if(h[i]<=list[j])
{
list[j]=h[i];
break;
}
}
if(j>list[0])list[++list[0]]=h[i];
}
printf("%d",list[0]);
return 0;
}