题目链接

题目大意:求一个数列的最长下降子序列长度,以及长度最长的下降子序列的数量

动态规划


题目分析:求最长下降子序列是常规操作了,可以直接用朴素\(O(n ^ 2)\)算法,也可以用二分做到\(O(nlogn)\)复杂度,不过\(n \leq 5000\)朴素算法随便跑

那么如何统计方案呢?

我们\(dp\)求最长下降子序列是设\(d(i)\)表示以\(i\)结尾的下降子序列的最长长度.同理,我们可以用\(f(i)\)表示以\(i\)结尾,长度为\(d(i)\)的下降子序列的个数

不难想到\(f(i) = \begin{cases} 1 \qquad d(i) == 1 \\ \sum_{j}^{i - 1}\{f(j)\;|\;j < i \;\;\&\&\;\;f(j) + 1 == f(i)\} \end{cases}\)

但是这么做是有问题的,原因在于重复统计

假如有\(d(i) == d(j)\)并且\(a(i) == a(j)\),且满足$i <j \(,有\)f(i) \leq f(j)$

道理很简单,因为\(f(i)\)的所有方案都被\(f(j)\)所包括了,这时你需要将\(f(j)\)置为\(0\),不然\(f(j)\)的方案会被统计多次

代码奉上,数据规模小就写的\(O(n ^ 2)\)算法

#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 8192;
int val[maxn],g[maxn],d[maxn],f[maxn],n,ans;//val为原数列,d[i]表示以i结尾的最长下降子序列长度,f[i]表示以i结尾,长度为d[i]的下降子序列的数量
int main(){
	ios::sync_with_stdio(false);
	cin >> n;
	for(int i = 1;i <= n;i++)
		cin >> val[i];
	for(int i = 1;i <= n;i++)
		g[i] = -0x7fffffff;
	ans = -1;
	for(int i = 1;i <= n;i++){
		int k = 0;
		int l = 1,r = n;
		while(l <= r){
			int mid = (l + r) >> 1;
			if(g[mid] > val[i])k = mid,l = mid + 1;
			else r = mid - 1;
		}
		d[i] = k + 1;
		g[k + 1] = val[i];
		ans = max(ans,d[i]);
	}
	cout << ans << " ";//求LIS,没啥好说的
	for(int i = 1;i <= n;i++){//依照转移方程
		if(d[i] == 1)f[i] = 1;
		for(int j = 1;j < i;j++)
			if(d[i] == d[j] && val[i] == val[j])//如果j的方案已经被i包含了
				f[j] = 0;
			else if(d[i] == d[j] + 1 && val[i] < val[j])//统计方案
				f[i] += f[j];
	}
	int sum = 0;
	for(int i = 1;i <= n;i++){//输出答案
		if(d[i] == ans)sum += f[i];
	}
	cout << sum << '\n';
	return 0;
}

相关文章:

  • 2022-01-15
  • 2021-05-24
  • 2022-02-02
  • 2022-12-23
  • 2021-06-15
  • 2022-02-10
  • 2022-12-23
猜你喜欢
  • 2021-06-09
  • 2022-12-23
  • 2021-05-28
  • 2021-12-21
  • 2021-11-22
相关资源
相似解决方案