题目描述
上下有两个长度为n、位置对应的序列A、B, 其中数的范围均为1~n。若abs(A[i]-B[j]) <= 4, 则A[i]与B[j]间可以连一条边。现要求在边与边不相交的情况下的最大的连边数量。 n <= 10^5
输入格式
第一行给出数字N 接下来N个数字,代表第一个数列 再接下来N个数字,代表第二个数列
输出格式
在边与边不相交的情况下的最大的连边数量
题意
现在有两行数字,保证全排列
两行任意两个数的差<=4即可连线
求每条线不相交的情况下,最多可连多少条线
解答
暴力DP
最开始想的是DFS暴力枚举然后又想到去推DP
dp[i][j]表示第一行选到第i个,第二行选到第j个的最大连线数
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
要么选,要么不选
可以选就:
if(abs(a[i]-a[j])<=4)dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
f[i][j]=max(dp[i-1][j],dp[i][j-1]);
if (abs(a[i]-b[j])<=4)dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1);
}
}
但由于10^5for嵌套必炸,所以要其他方法
LIS
然后考试也没想到lower_bound去做LIS
考后,个人认为这里的转换很巧妙
将当前这个数可以连接的另一行的数的位置依次存入t数组内
由于连接的线段不相交,所以从前往后必定严格上升
这不难想到可以用LIS
把当前这个数可以连接的另一行的数的位置依次存入t数组内
为了不让这个数同时连另一行的多个数
所以依次存入t数组从大到小排序,用sort+cmp重载运算符
最后lower_bound求t数组的最长上升子序列即可
#include<bits/stdc++.h>
using namespace std;
int n,ans,sum,tot;
int a[100010],b[100100];
int t[100010];
int c[100];
bool cmp(int a,int b) {
return a>b;
}
int main() {
//freopen("line.in","r",stdin);
//freopen("line.out","w",stdout);
cin>>n;
for(int i=1; i<=n; i++){
cin>>a[i];
}
for(int i=1; i<=n; i++) {
int x;
cin>>x;
b[x]=i;
}
for(int i=1; i<=n; i++) {
int l=0;
int head=max(1,a[i]-4);
int tail=min(n,a[i]+4);
for(int j=head;j<=tail;j++){
l++;
c[l]=b[j];
}
sort(c+1,c+l+1,cmp);
for(int j=1;j<=l;j++) {
if(tot==0){
tot++;
t[tot]=c[j];
}
else {
if(c[j]>t[tot]) {
tot++;
t[tot]=c[j];
}else{
int y=lower_bound(t+1,t+tot+1,c[j])-t;
t[y]=c[j];
}
}
}
}
cout<<tot;
return 0;
}
lower_bound简介
lower_bound是STL的一个函数,也是[左闭右开)结构
然后用的是二分查找的方法,返回的该数字的地址
以后补LIS等题目和lower/upper_bound做法的题解