连线

传送门

题目描述
上下有两个长度为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做法的题解

原文地址:https://www.cnblogs.com/pqh-/p/13928670.html