团体程序设计天梯赛 决赛 L2 列车调度

最长上升子序列与Dilworth定理
本文探讨了如何通过求解最长上升子序列问题来找出序列中最小数量的下降子序列,并利用Dilworth定理进行证明。文中还提供了一段C++代码示例,展示了求解最长上升子序列的具体实现。

题目链接:https://www.patest.cn/contests/gplt/L2-014

题意:让我们求出这个序列最少的下降序列个数,而最少的下降序列个数就等于整个序列最长上升子序列的长度。

由于小编的疏忽,觉得这就是个人人皆知的定理,没有给出证明过程,让读者感到了困惑,这里小编由衷的道歉,同时感谢A_LeiQ同学的提醒,这里小编给出一个证明过程。

其实这个证明过程得提一个定理,那就是偏序集的Dilworth定理,关于偏序集,离散数学中的概念,这里限于篇幅,小编不在详细介绍,这里证明一下这个在这个地方的Dilworth定理的运用。

首先,我们设一个数a的值小于等于另一个数b且它的位置在这个数的前面(或同一个位置)表示a≤b,用S表示整个集合,用极小元来表示只有自己能和自己确立≤关系的数,用链表示一个子集,里面的元素互相都能确定≤的关系,用反链来表示一个子集,里面的元素互相都不能确定≤的关系,我们设最少反链划分数是p,最长链长度是r。

1.先证p≥r。这是显然的,因为最长链长度是r,r个元素中的任意两个都可以比较,因此它们必定两两属于不同的反链,因此反链个数≥r,即p≥r。用这个例子来说的话就是,一个上升序列中每一个数肯定不可能出现在同一个下降子序列中。

2.再证r≥p。设X1=S。找出X1的所有极小元组成集合Z1,将其从X1删之,得到X2,再找出X2的所有极小元组成集合Z2(特别注意X2中的任何元素a2,在X1中必然存在一个元素a1使得a1≤a2,否则a2可以放到X1中,这与X1的选取矛盾),再将Z2从X2中删除,得到X3,……这样一直下去,总存在一个k使得XK不空但X(K+1)为空。这样便得到一条链a1,a2,a3,……,ak,其中ai属于Xi。由于r是最长链长度,因此r≥k。另一方面,我们也得到了一个反链划分,即X1,X2,X3,……,XK。由于p是最少反链划分,因此k≥p。因此有r≥p。这里不懂的同学可以就拿样例来模拟一下,很容易就能发现这个证明过程的正确性。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 100000+5;
int a[maxn], dp[maxn];
int main()
{
	int n;
	scanf("%d", &n);
	for(int i=0; i<n; i++) scanf("%d", &a[i]);
	dp[0] = a[0];
	int num = 0;
	for(int i=1; i<n; i++)
		if(dp[num] < a[i])
			dp[++num] = a[i];
		else{
			int pos = upper_bound(dp,dp+num,a[i])-dp;
			dp[pos] = a[i];
		}
	printf("%d\n", num+1);
}


### PTA 团体程序设计天梯赛 L2 级别题目解析 #### 并查集的应用实例——功夫传人 对于PTA团体程序设计天梯赛中的L2-020 功夫传人这道题而言,采用并查集是一种有效的解决方案。此方法通过维护两个数组来实现:一个是用于储存每位成员当前所拥有的功力值;另一个则记录下飞升者在其家族树上的位置以及他们各自对应的功力增长系数[^1]。 当处理飞升者的特殊情况时,需注意其功力计算方式不同于普通成员。具体来说,在遇到一位直接由师父指导而成仙的情况(即徒弟编号紧接于师父之后),该弟子的最终功力应等于初始基础值乘以其特定的增长因子r而非累加其他贡献后的产物。这意味着假如某位编号为2的人物成为了飞升者,并且其师尊位于序列起始处(假设为0),那么此人获得的新能力应当被设定为基础数值18与比例常量r相乘的结果,而不是先经历常规增益再做调整。 此外,值得注意的是本题目的四舍五入机制并非传统意义上的数学运算,而是采取了一种更为简单的截断策略—即将任何非整数部分无条件去除只保留整数部分作为结果输出。 ```cpp #include <iostream> using namespace std; const int MAXN = 1e5 + 7; int power[MAXN], ratio[MAXN]; bool isImmortal[MAXN]; // 查找根节点的同时进行路径压缩优化 int find(int x){ if(x != power[x]) power[x] = find(power[x]); return power[x]; } void unionSet(int x, int y, double r){ int rootX = find(x), rootY = find(y); if(rootX == rootY) return ; // 更新新加入集合中所有元素的比例关系 for (int i = 1; i <= n ; ++i) if(find(i)==rootY) ratio[i]*=r; power[rootY]=rootX; } ``` 上述代码片段展示了如何利用并查集结构解决这个问题的核心逻辑。这里定义了一个`find`函数用来查找某个节点所属集合的代表元,并实现了路径压缩以提高查询效率;而`unionSet`负责合并不同集合并将新的比率应用给受影响的所有成员。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值