【最长上升子序列】拦截导弹

题目链接

点我进入题目

题目大意

给定一串序列,问: ①求最长下降子序列长度 ②求这个序列最少由多少组下降子序列构成。

思路

  • 判断是否为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 ABAB
A ≥ B A \geq B AB,因为B为最优解,那么B肯定是最小的数,即 A ≥ B A \geq B AB,得证。
A ≤ B A \leq B AB
设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 AB,得证。

思路实现:
以长度作为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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值