owned this note
owned this note
Published
Linked with GitHub
# lIS
全名longest increasing subsequence,最長遞增子序列
### 1.Robinson-Schensted-Knuth Algorithm
概念:以greedy為基礎,二元搜尋加速
1. 初始作業
```cpp=
//測資:a[]
vector<int> stk{a[0]},len(a.size());
//先放入一個數避免取stk.back()時segmentation fault
len[0]=1;
```
2. 遍歷一次a,如果該元素大於stk.back()的話就放入尾端
否則就用lower_bound找到第一個大於或等於該元素的元素,並把他替換掉(該元素成為lis的淺能比lower_bound回傳元素的淺能還大)
```cpp=
for(int i=1;i<a.size();i++){
if(a[i]>stk.back()){
stk.push_back(a[i]);
len[i]=stk.size();
}else{
auto it=lower_bound(stk.begin(),stk.end(),a[i]);
*it=a[i];
len[i]=it-stk.begin()+1;
}
}
```
3. 從尾開始到回去找,因為這樣才能找到成為lis淺能最大的,先設一個變數=LIS,接著從len的最後倒回去找如果len[i]==now,就代表在做LIS時有放進一個元素a[i],該元素屬於LIS,所以要把他加到ans
```cpp=
vector<int> ans;
int now=stk.size();
for(int i=a.size()-1;i>=0;i--){
if(len[i]==now){
ans.push_back(a[i]);
now--;
}
}
}
```
4. 最後顛倒過來就是正的LIS了
```cpp=
reverse(ans.begin(),ans.end());
for(auto i:ans){
cout << i << " ";
}
```
#非嚴格遞增子序列要使用upper_bound#
說明:因為同為value的值可以出現很多次,所以要找大於value中最小的值來替換
```cpp=
for(int i=1;i<testlen;i++){
if(test[i]>dparr.back()){
dparr.push_back(test[i]);
lislen++;
dp[i]=lislen;
}else{
auto it=upper_bound(dparr.begin(),dparr.end(),test[i]);
*it=test[i];
dp[i]=it-dparr.begin()+1;
}
}
```
另一種寫法
```cpp=
for(int i=0;i<testlen;i++){
int value=test[i];
int len=upper_bound(dparr.begin(),dparr.end(),value)-dparr.begin();
if(len==aparr.size()){ //找不到
dparr.push_back(value);
}else{
test[len]=value;
}
}
```
<a href="https://zerojudge.tw/ShowProblem?problemid=d242">ZJ d242: 00481 - What Goes Up</a>
```cpp=
#include <bits/stdc++.h>
using namespace std;
int main(){
int x;
vector<int> a;
while(cin >> x) a.push_back(x);
vector<int> stk{a[0]},len(a.size());
len[0]=1;
for(int i=1;i<a.size();i++){
if(a[i]>stk.back()){
stk.push_back(a[i]);
len[i]=stk.size();
}else{
auto it=lower_bound(stk.begin(),stk.end(),a[i]);
*it=a[i];
len[i]=it-stk.begin()+1;
}
}
cout << stk.size() << '\n' << "-" << '\n';
vector<int> ans;
int now=stk.size();
for(int i=a.size()-1;i>=0;i--){
if(len[i]==now){
ans.push_back(a[i]);
now--;
}
}
for(int i=ans.size()-1;i>=0;i--){
cout << ans[i] << '\n';
}
return 0;
}
```
### 2.Dynamic Programming
看當前元素有沒有比前面的元素小,有的話,代表LIS可以變長,就把LIS更新到當前的陣列裡
DP狀態轉移:
```
if(dp[j]<dp[i])
dp[i]=max(dp[i],dp[j]+1) for j in range(0,i-1);
```
Code:
```cpp=
int getlcs(vector<int> &nums){
int n=nums.size();
vector<int> lis(n,1); //每個LIS初始都是自己一個
int ans=0;
for(int i=0;i<n;i++){
for(int j=0;j<i;j++){
if(nums[i]>nums[j]){ //如果當前元素nums[i]有大於前面的任何一個元素nums[j]
lis[i]=max(lis[i],lis[j]+1); //那就更新到lis[i]=前面元素nums[j]的lis+1
}
}
ans=max(ans,lis[i]); //更新最大值
}
return ans;
}
```
題目:<a href="https://zerojudge.tw/ShowProblem?problemid=c115">c115: 00437 - The Tower of Babylon</a>
Solution 1:看前面有沒有長寬都比我小的,如果有代表可以疊上去,就把答案更新到lis[i]裡面
```cpp=
vector<int> lis(n*3,0);
int ans=0;
for(int i=0;i<n*3;i++){
lis[i]=arr[i].h; //初始化,每個lis都是自己
for(int j=0;j<i;j++){
if(arr[i].x<arr[j].x && arr[i].y<arr[j].y){
lis[i]=max(lis[i],lis[j]+arr[i].h);
}
}
ans=max(ans,lis[i]);
}
```
suoution 2:看我後面有沒有長寬都比我小的,如果有代表後面那個方塊可以疊加到當前方塊身上,就把當前的高度更新到lis[j]
```cpp=
vector<int> lis(n*3,0);
int ans=0;
for(int i=0;i<n*3;i++){
lis[i]+=arr[i].h; //把前面最高的高度加上自己的高度就是當前最高的高度
for(int j=i+1;j<n*3;j++){
if(arr[i].x>arr[j].x && arr[i].y>arr[j].y){
lis[j]=max(lis[i],lis[j]);
}
}
ans=max(ans,lis[i]);
}
```
Code :
```cpp=
#include <bits/stdc++.h>
using namespace std;
struct node{
int x,y,h;
};
bool cmp(node a,node b){
if(a.x==b.x) return a.y>b.y;
return a.x>b.x;
}
int main(){
int n,a,b,c,casen=1;
while(cin >> n && n){
vector<node> arr(n*3);
for(int i=0;i<n;i++){
cin >> a >> b >> c;
arr.push_back({max(a,b),min(a,b),c});
arr.push_back({max(a,c),min(a,c),b});
arr.push_back({max(b,c),min(b,c),a});
}
sort(arr.begin(),arr.end(),cmp);
vector<int> lis(n*3,0);
int ans=0;
for(int i=0;i<n*3;i++){
lis[i]+=arr[i].h;
for(int j=i+1;j<n*3;j++){
if(arr[i].x>arr[j].x && arr[i].y>arr[j].y){
lis[j]=max(lis[i],lis[j]);
}
}
ans=max(ans,lis[i]);
}
cout << "Case " << casen++ <<": maximum height = " << ans << '\n';
}
return 0;
}
```