程序员的背包 ZZULIOJ - 2485 离散化 dp lis | 树状数组

题解

经典的最长上升子序列问题
数值过大使用离散化处理 数值过多使用二分优化dp

AC代码

#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const int MAXN = 5e4 + 10;
int a[MAXN], d[MAXN], p[MAXN]; //d[i]以i为结尾的上升子序列长度 p[i]长度为i的最小结尾
vector<int> dz;

int Dis(int v)
{
	return lower_bound(dz.begin(), dz.end(), v) - dz.begin();
}
int main()
{
#ifdef LOCAL
	//freopen("C:/input.txt", "r", stdin);
#endif
	int N;
	cin >> N;
	for (int i = 1; i <= N; i++)
		scanf("%d", &a[i]), dz.push_back(a[i]);
	dz.push_back(-INF);
	sort(dz.begin(), dz.end());
	dz.erase(unique(dz.begin(), dz.end()), dz.end()); //数值过大 离散化
	memset(p, 0x3f, sizeof(p));
	p[0] = -INF;
	int ans = 0;
	for (int i = 1; i <= N; i++)
	{
		int k = lower_bound(p, p + N, Dis(a[i])) - p; //第一个大于等于当前值的长度
		d[i] = k;
		p[k] = min(p[k], Dis(a[i])); //更新p数组
		ans = max(ans, d[i]);
	}
	cout << ans << endl;

	return 0;
}

附带最开始蒙比了写的树状数组解法

#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const int MAXN = 5e4 + 10;
int a[MAXN], c[MAXN];
vector<int> dz;

inline int Lowbit(int x) //x二进制最低一位的1的值
{
	return x & -x;
}
void Add(int x, int v, int n) //修改位置 修改值 最大下标 使用Add初始化c数组 要求下标范围从1开始
{
	while (x <= n)
		c[x] = max(c[x], v), x += Lowbit(x); //x + Lowbit(x) 父节点
}
int Ask(int x) //查询1~x前缀和
{
	int res = 0;
	while (x)
		res = max(res, c[x]), x -= Lowbit(x); //x -= Lowbit(x) 兄弟节点
	return res;
}
int Dis(int v)
{
	return lower_bound(dz.begin(), dz.end(), v) - dz.begin();
}
int main()
{
#ifdef LOCAL
	//freopen("C:/input.txt", "r", stdin);
#endif
	int N;
	cin >> N;
	for (int i = 1; i <= N; i++)
		scanf("%d", &a[i]), dz.push_back(a[i]);
	dz.push_back(-INF);
	sort(dz.begin(), dz.end());
	dz.erase(unique(dz.begin(), dz.end()), dz.end());
	int ans = 0;
	for (int i = 1; i <= N; i++)
	{
		int k = Dis(a[i]); //离散化
		int res = Ask(k - 1); //查找小于k的最长长度
		Add(k, res + 1, dz.size()); //记录当前长度
		ans = max(ans, res + 1);
	}
	cout << ans << endl;

	return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值