题目链接:
P1233 ICPC 2001 Taejon R 木棍加工 - 洛谷

思路:求一个数列中最长严格上升子序列的最大长度
代码:
cpp
#include<bits/stdc++.h>
using namespace std;
int n,ans = 0;
int main()
{
cin >> n;
vector<pair<int,int>> arr(n);
for(int i = 0;i <n ;i++) cin >> arr[i].first >> arr[i].second;
auto cmp = [](pair<int,int> a,pair<int,int> b){
if(a.first != b.first)return a.first > b.first;
return a.second > b.second;
};
sort(arr.begin(),arr.end(),cmp);
vector<int> dp(n,1);
for(int i = 0;i < n ;i++)
{
for(int j = 0;j < i;j++)
{
if(arr[i].second > arr[j].second) dp[i] = max(dp[i],dp[j] + 1);
}
ans = max(ans,dp[i]);
}
cout << ans;
return 0;
}
首先将数组以第一个键进行排序 这样就保证了长度在数列是严格递减的
然后就是处理宽度的问题:
排完之后,看宽度的序列。比如样例:
长度降序排完:(5,2) (4,9) (3,5) (2,1) (1,4)
宽度序列: 2 9 5 1 4
现在的问题:把这个序列分成尽量少的"不上升"子串。
2, 1 ← 一条
9, 5, 4 ← 一条
两条。 所以答案是 2。
为什么不升子串的条数 = 最长上升子序列的长度
例子
排好的序列: 3 1 4 2
位置: 0 1 2 3
问题 :把这四个数分成尽量少的几堆,每一堆从左往右不许上升(只能递减或相等)。
先算一个东西:每个位置"能被多长的上升串接上"
记号 dp[i]:
以第 i 个数结尾的、最长的上升子串,有多长。
i=0,数 3: 前面没人,光它自己 → dp = 1
i=1,数 1: 前面只有 3,3 比 1 大,接不上 → dp = 1
i=2,数 4: 3<4 能接(1+1=2);1<4 能接(1+1=2) → dp = 2
i=3,数 2: 1<2 能接(1+1=2);3、4 都比它大 → dp = 2
数: 3 1 4 2
dp: 1 1 2 2
最大的 dp 是 2。 所以最长上升子序列长度 L = 2 (比如 3,4,或者 1,2)。
现在按 dp 的值分堆
dp = 1 的: 3 1
dp = 2 的: 4 2
看这两堆里面:
3 → 1 不上升 ✓
4 → 2 不上升 ✓
两堆,每堆都合法。分完了。
为什么"同一堆一定不上升"
这是整件事的关键
假设同一堆里,出现了两个数,后面的比前面的大:
... a ... b ... 且 a < b,且 dp 相同
那 b 的 dp 怎么可能是同一个?
因为 a < b,b 就能把"以 a 结尾的那条上升串"整个接过来 ,长度是 dp[a] + 1。
这比 dp[b] 大,矛盾。
所以:同一堆里,后面的数不可能比前面大。 只能一路不增。
这就是为什么"按 dp 分堆"是合法的。
那堆数为什么正好是 L?
dp 的取值,最小是 1,最大是 L。
所以最多只有 L 堆(1、2、...、L)。
再看"为什么不能少于 L"
找一条最长的上升子序列,长度是 L。
比如 3, 4。
这两个数,能待在同一堆里吗?
不能。 因为 3 < 4,是上升的------而同一堆里不许上升。
所以这条 L 长的子序列里,每个数都必须独占一堆。
堆数 ≥ L。
两边一夹
堆数 ≥ L (上升子序列逼出来的)
堆数 ≤ L (按 dp 分,最多 L 堆)
→ 堆数 = L
知道这个后 这个题就简单了