P8736 [蓝桥杯 2020 国 B] 游园安排

P8736 [蓝桥杯 2020 国 B] 游园安排

一、问题简析

本题考点是 最长上升子序列

二分查找

根据模板,我们需要实现一个二分查找,找到 dp 中首个大于等于 A[i] 的元素。比较的规则是字典序

// 按字典序比较A[a]和A[b],return A[a] < A[b] 
bool cmp(const int &a, const int &b)
{
	int mmin = min(cnt[a], cnt[b]);
	for (int i = 0; i < mmin; ++i)
		if (A[a][i] > A[b][i])    return false;
		else if (A[a][i] < A[b][i])    return true;
	if (cnt[a] < cnt[b])    return true;
	else    return false;
}

// 二分查找dp[i]中首个不小于A[key]的位置 
int search(int key)
{	
	int L = 1, R = ptr;
	while (L <= R)
	{
		int M = (R - L) / 2 + L;
		if (cmp(dp[M], key))
			L = M + 1;
		else
			R = M - 1;
	}
		
	return L;
}

哨兵元素

在模板中,需要将 dp 都初始化为 INF。此出,按字典序,INFZzzzzzzzzzzz。原因有两点:1、名字都是由一个大写字母若干个小写字母构成;2、名字最长为10位

记录子序列

模板中,dp 仅记录尾元素。在讨论元素 A[i] 时,二分找到了它在 dp 中的位置 j,一定可以确定 dp[j - 1] 是它的前一个元素的下标。因此,我们只要记录每一个元素的前一个元素。最后,从最长上升子序列的尾元素开始,倒着找到所有元素。


Code

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

const int MAX = 1e6 + 5;
int dp[MAX], pre[MAX], cnt[MAX], ptr;  // ptr -- 元素个数 
// dp[i] -- 长度i的序列的尾元素下标; pre[i] -- 下标i的元素的前一个元素的下标; cnt[i] -- 下标i的元素的长度 
char A[MAX][15], s; // A[i] -- 下标i的元素; s -- 输入 

// 按字典序比较A[a]和A[b],return A[a] < A[b] 
bool cmp(const int &a, const int &b)
{
	int mmin = min(cnt[a], cnt[b]);
	for (int i = 0; i < mmin; ++i)
		if (A[a][i] > A[b][i])    return false;
		else if (A[a][i] < A[b][i])    return true;
	if (cnt[a] < cnt[b])    return true;
	else    return false;
}

// 二分查找dp[i]中首个不小于A[key]的位置 
int search(int key)
{	
	int L = 1, R = ptr;
	while (L <= R)
	{
		int M = (R - L) / 2 + L;
		if (cmp(dp[M], key))
			L = M + 1;
		else
			R = M - 1;
	}
		
	return L;
}

int main()
{
	#ifdef LOCAL
	freopen("test.in", "r", stdin);
	#endif
	
	char ch;
	while (true)
	{
		ch = getchar();
		if (ch == EOF)    break;
		
		if (ch >= 'A' && ch <= 'Z')    ++ptr; // 将输入元素分开 
		
		A[ptr][cnt[ptr]++] = ch;
	}
	
	sprintf(A[0], "Zzzzzzzzzzzz"); // 相当于INF 
	cnt[0] = 12;
	for (int i = 1; i <= ptr; ++i)
	{
		int ans = search(i);
		
		dp[ans] = i;
		pre[i] = dp[ans - 1]; // 记录A[i]的前一个元素下标 
	}
	
	int p = search(0);
	p = dp[p - 1];     // 首个小于INF的元素A[p],即最长子序列的尾元素 
	stack<int> ans;
	while (p != 0)     // 逆序找到子序列中的所有元素 
	{
		ans.push(p);
		p = pre[p];
	}
	
	while (!ans.empty())
	{
		printf("%s", A[ans.top()]);
		ans.pop();
	}
	
	return 0;
}

posted @   ltign  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示