## LIS问题及其应用
模板
这里给出贪心+二分(n log n),dp(n²)两种做法。
//动态规划O(n²)
#include<iostream>
#include<algorithm>
using namespace std;
int n;
const int MAX=1010;
int a[MAX];
int dp[MAX];
int main(){
cin>>n;
for (int i=1;i<=n;i++)
cin>>a[i];
for (int i=1;i<=n;i++){
dp[i]=1;
for (int j=1;j<i;j++)
if (a[i]>a[j])
dp[i]=max(dp[i],dp[j]+1);
}
int res=0;
for (int i=1;i<=n;i++)
res=max(res,dp[i]);
cout<<res<<endl;
return 0;
}
#include<iostream>
#include<algorithm>
using namespace std;
int n;
const int MAX=10010;
int a[MAX],t[MAX];
int main(){
cin>>n;
for (int i=1;i<=n;i++) cin>>a[i];
int len=0;
for (int i=1;i<=n;i++){
int l=0,r=len;
while (r>l){ //二分:小于a[i]的最大数,如果没有为其创建新位置
int mid=l+r+1>>1;
if (t[mid]<a[i]) l=mid;
else r=mid-1;
}
t[r+1]=a[i];
len=max(len,r+1);
}
cout<<len<<endl;
return 0;
}
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int dp[1001];
int a[1001];
int main(){
int input;
int n;
scanf("%d",&input);
while (input--){
int result=0;
scanf("%d",&n);
for (int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for (int i=1;i<=n;i++){
dp[i]=1;
for (int j=1;j<i;j++){
if (a[i]>a[j])
dp[i]=max(dp[i],dp[j]+1);
}
result=max(result,dp[i]);
}
for (int i=n;i>=1;i--){
dp[i]=1;
for (int j=n;j>i;j--){
if (a[i]>a[j])
dp[i]=max(dp[i],dp[j]+1);
}
result=max(result,dp[i]);
}
printf("%d\n",result);
}
return 0;
}
思路:预处理出以每个点为结尾的LIS,和以每个点为开头的下降序列。再通过暴力枚举出每一种情况。
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int n;
const int MAX=1100;
int high[MAX],higher[MAX],lower[MAX],res=0;
int main(){
cin>>n;
for (int i=1;i<=n;i++)
cin>>high[i];
for (int i=1;i<=n;i++){
higher[i]=1;
for (int j=1;j<i;j++){
if (high[i]>high[j])
higher[i]=max(higher[i],higher[j]+1);
}
}
for (int i=n;i;i--){
lower[i]=1;
for (int j=i+1;j<=n;j++){
if (high[j]<high[i])
lower[i]=max(lower[i],lower[j]+1);
}
}
for (int i=1;i<=n;i++)
res=max(res,higher[i]+lower[i]-1);
cout<<res<<endl;
return 0;
}
合唱队形(爆搜+LIS)
思路:
要想剔除的人最少,就需要对留下的人最多。根据题意,每个队列都会有一个中间点。(这个点左边的点比他小,右边的点比他大),那么我们先预处理出以每个点作为中间点时,比它小的点的个数和比他大的点的个数。用上一题的做法求出队列中最多剩下的人数之后,再用n(总共的人数)-这个数 就是答案。
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int n;
const int MAX=110;
int a[MAX];
int higher[MAX],lower[MAX];
int main(){
cin>>n;
for (int i=1;i<=n;i++)
cin>>a[i];
for (int i=1;i<=n;i++){
higher[i]=1;
for (int j=1;j<i;j++)
if (a[i]>a[j])
higher[i]=max(higher[i],higher[j]+1);
}
for (int i=n;i;i--){
lower[i]=1;
for (int j=i+1;j<=n;j++)
if (a[j]<a[i])
lower[i]=max(lower[i],lower[j]+1);
}
int res=0;
for (int i=1;i<=n;i++)
res=max(res,lower[i]+higher[i]-1);
cout<<n-res<<endl;
return 0;
}
友好城市 转换题LIS+sort
思路: 本题的做法是先按照其友好城市的坐标sort一遍,再做一遍LIS。 接下来说明为什么要这么做:
1.友好城市之间的线路不能交叉,就代表着两个友好城市的坐标必须同时单调递增。
2.再加上要求最多的城市对数,很容易想到要使用最长上升子序列。既然要两者同时保持单调性(自然可以基于其友好城市对其进行排序,两个城市同时LIS应该也可以)。
3.最后一边LIS就是答案。
//经过这一题,要注意算法题中单调性,无论是条件还是特性都十分重要。
#include<iostream>
#include<algorithm>
using namespace std;
typedef pair<int ,int > PII;
int n;
const int MAX=5100;
int dp[MAX];
PII a[MAX];
int main(){
cin>>n;
for (int i=1;i<=n;i++){
cin>>a[i].first>>a[i].second;
}
sort(a+1,a+n+1);
int res=0;
for (int i=1;i<=n;i++){
dp[i]=1;
for (int j=1;j<i;j++){
if (a[i].second>a[j].second)
dp[i]=max(dp[i],dp[j]+1);
}
res=max(dp[i],res);
}
cout<<res<<endl;
return 0;
}
//dp的思想和考虑方式和LIS问题一样。
#include<iostream>
#include<algorithm>
using namespace std;
int n;
const int MAX=10010;
int a[MAX],dp[MAX];
int main(){
cin>>n;
int res=0;
for (int i=1;i<=n;i++) cin>>a[i];
for (int i=1;i<=n;i++){
dp[i]=a[i];
for (int j=1;j<i;j++){
if (a[i]>a[j])
dp[i]=max(dp[i],dp[j]+a[i]);
}
res=max(res,dp[i]);
}
cout<<res<<endl;
return 0;
}
LIS问题性质的探索 Dilworth定理
//关于LIS问题有一个非常有趣的性质:把整个序列用其下降子序列填满需要的上升子序列数=该序列的最长上升子序列。
(类似,用上升子序列填满就=最长下降子序列长度)
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAX=10010;
int a[MAX],up[MAX],down[MAX];
int main(){
int idx=1;
while (cin>>a[idx],a[idx]) idx++;
int res1=0,res2=0;
for (int i=1;i<idx;i++){
up[i]=1,down[i]=1;
for (int j=1;j<i;j++){
if (a[i]<=a[j])
up[i]=max(up[i],up[j]+1);
else if (a[i]>=a[j])
down[i]=max(down[i],down[j]+1);
}
res1=max(up[i],res1);
res2=max(res2,down[i]);
}
cout<<res1<<endl<<res2<<endl;
return 0;
}