题目链接
题目大意
给定一串序列,问: ①求最长下降子序列长度 ②求这个序列最少由多少组下降子序列构成。
思路
- 判断是否为DP题
必DP题
- 判断DP类型
最长下降子序列
- 闫氏DP思考
求第一问:
集合:前i个数中下降子序列长度。
属性:max
划分依据:该数 与 前面比该数大的数
动态转移方程:
f
[
i
]
=
m
a
x
(
f
[
i
]
,
f
[
j
]
+
1
)
f[i] = max(f[i], f[j] + 1)
f[i]=max(f[i],f[j]+1)
求第二问:
DP思想已经尝试,现在还需求最优化问题,尝试使用贪心思想。
写贪心题的时候, 先按照直觉将思路写下来:
① 当前面的数没有比当前数大时,新建一个系统。
② 当前面的数有比当前数大时,把这个数放到比这个数大的数中的最小的数的后面。
然后证明这个贪心思路:
设A为贪心思想的解, B为最优解。
欲证:
A
=
B
A = B
A=B, 一般想法是证:
A
≥
B
和
A
≤
B
A \geq B 和 A \leq B
A≥B和A≤B
证
A
≥
B
A \geq B
A≥B,因为B为最优解,那么B肯定是最小的数,即
A
≥
B
A \geq B
A≥B,得证。
证
A
≤
B
A \leq B
A≤B:
设A、B两个序列不一样。
找到AB第一个不同的数, A 为 b, B 为 a, 他们后面的数为x。
因为 A x前面的一个数是比他大的最小的一个数,所以
a
>
=
b
a >= b
a>=b。
我们将b字符后面的字符串与 a字符后面一串的字符串交换后发现,B的个数并没有变化,说明B序列可以经过调整变成A,即:
A
≤
B
A \leq B
A≤B,得证。
思路实现:
以长度作为x轴,每个序列最后一位数作为y轴,在坐标轴上一画,发现呈现单调递增的情况,与最长上升子序列中的二分写法思路一样。
所以就是最长上升子序列。
代码很简单,重要的是这个过程!
AC代码
#include<bits/stdc++.h>
using namespace std;
#define _for(i, a, b) for (int i = (a); i < (b); ++i)
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define For(i, a, b) for (int i = (a); i >= (b); --i)
#define debug(a) cout << #a << " = " << a << endl;
#define mod(x) (x) % MOD
#define ENDL "\n"
typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi;
const int N = 1000 + 10;
int w[N], f[N], g[N];
int main()
{
#ifdef LOCAL
freopen("data.in", "r", stdin);
#endif
ios::sync_with_stdio(false);
cout.tie(0);
cin.tie(0);
int n = 0;
while (cin >> w[n]) n++;
int ans = 0, cnt = 0;
_for (i, 0, n)
{
f[i] = g[i] = 1;
_for(j, 0, i) if (w[j] >= w[i]) f[i] = max(f[i], f[j] + 1);
_for(j, 0, i) if (w[j] < w[i]) g[i] = max(g[i], g[j] + 1);
ans = max(ans, f[i]);
cnt = max(cnt, g[i]);
}
cout << ans << ENDL << cnt << ENDL;
return 0;
}