题目描述
有一批大朋友(年龄 15 岁以上),他们每人手上拿着一个数字,当然这个数字只有 1 位,也就是 0 到 9 之间。每个大朋友的分数为在他之前的最长不下降子序列中所有数之和。(这个序列必须以它作为结尾!)如有多个最长不下降子序列,那么取编号字典序最小的。现在告诉你有 n 个大朋友,以及他们各自的数字,请你求出他们每个人的分数。
输入格式
第一行,1个数 n。
第二行,n 个数,分别表示每个人的数字。
输出格式
一行,n 个数,分别表示每个人的分数。
输入输出样例
输入 #1
5 1 2 5 3 4输出 #1
1 3 8 6 10输入 #2
5 1 7 5 9 6输出 #2
1 8 6 17 12
说明/提示
【样例解释 1】
五个人分数分别为 (1),(1+2),(1+2+5),(1+2+3),(1+2+3+4)。
【样例解释 2】
五个人分数分别为 (1),(1+7),(1+5),(1+7+9) (还有一个 (1,5,9)),(1+5+6)。
【数据规模】
对于 50% 的数据,1≤n≤500;
对于 80% 的数据,1≤n≤10³;
对于 100% 的数据,1≤n≤104。
线性DP模板题最长不下降子序列,用 f[i] 表示以 i 结尾的最长不下降子序列,用 q[i] 表示以 i 结尾的最长不下降子序列的和。
状态转移方程:
从前往后枚举 i ,f[i] 初始长度至少为 1 ,再从前往后枚举 j 寻找 i 之前的所有方案,如果有 a[j] < a[i] ,那么 f[i] 必定可以在 j 的基础上加 1 ,最终与自身取最大值求解最优解,
即 f[i] = max(f[i],f[j]+1) ,同理,如果 f[i] 可以转移,那么最长上升子序列的和 q[i] 也可以转移,
即 q[i] = q[j] + a[i] 。
刚开始做的时候理解错了题意当作最长上升子序列来做,结果Wa了几次也没找到错误。
代码:
#include <iostream>
#include <cstdio>
using namespace std;
const int N=1e4+10;
int a[N];
int f[N];
int q[N];
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
{
f[i]=1;
for(int j=1;j<i;j++)
{
if(a[j]<=a[i])
{
if(f[i]<f[j]+1)
{
f[i]=f[j]+1;
q[i]=q[j];
}
}
}
q[i]+=a[i];
}
for(int i=1;i<=n;i++) printf("%d ",q[i]);
return 0;
}