BZOJ4990 (LCS转LIS)

题面

https://www.lydsy.com/JudgeOnline/problem.php?id=4990

分析

首先可以看出一个简单的DP
dp[i][j]表示序列a前i个与序列b前j个连线数量
d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ] , d p [ i − 1 ] [ j − 1 ] ( ∣ a [ i ] − b [ j ] ∣ &lt; = 4 ) ) dp[i][j]=max(dp[i-1][j],dp[i][j-1],dp[i-1][j-1]( \left| a[i]-b[j]\right|&lt;=4)) dp[i][j]=max(dp[i1][j],dp[i][j1],dp[i1][j1](a[i]b[j]<=4))
这样DP的时间复杂度为 O ( n 2 ) O(n^2) O(n2)
发现该方程除了转移的判断条件之外和LCS并无什么不同,因此可考虑LCS的优化方法

提示:阅读下面内容前,请先确保自己掌握一般情况下LCS转LIS的过程,以及LIS的 O ( n l o g 2 n ) O(nlog_2n) O(nlog2n)算法

考虑LCS转LIS,原本的方法是记录a[i]中每个值的位置pos,将b[i]转化为pos[b[i]]
既然 ∣ a [ i ] − b [ j ] ∣ &lt; = 4 \left| a[i]-b[j]\right|&lt;=4 a[i]b[j]<=4都可杯看做“相等”
则我们对于每个b[i]±j (0<=j<=4),将pos[b[i]±j]加入数组c,求c的LIS即为答案
但注意到每个点只能连一条边,也就是对于每个b[i],9个b[i]±j中只能选一个加入LIS
所以将9个一组从大到小排序,再拼起来,这样每组数中至多有一个数被选进LIS,(若选两个,则c[i]>c[i+1],矛盾)
时间复杂度 O ( n l o g 2 n ) O(nlog_2n) O(nlog2n)

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define maxn 100005
using namespace std;
int n;
int a[maxn],b[maxn];
int pos[maxn];
vector<int>tmp;
vector<int>c;
int s[maxn*9];
int m;
int cmp(int x,int y) {
	return x>y;
}
int solve() {
	for(int i=1;i<=n;i++){
		tmp.clear();
		for(int j=0;j<=4;j++){
			if(b[i]+j<=n) tmp.push_back(pos[b[i]+j]);
			if(b[i]-j>=1) tmp.push_back(pos[b[i]-j]);
		}
		sort(tmp.begin(),tmp.end(),cmp);
		int t=tmp.size();
		for(int j=0;j<t;j++){
			c.push_back(tmp[j]);
		}
	}
	int m=c.size();
//	for(int i=0;i<m;i++) printf("%d ",c[i]);
//	printf("\n");
	int top=0;
	for(int i=0; i<m; i++) {
		if(c[i]>s[top]) {
			s[++top]=c[i];
		} else {
			int tmp=lower_bound(s+1,s+1+top,c[i])-s;
			s[tmp]=c[i];
		}
	}
	return top;
}

int main() {
	scanf("%d",&n);
	for(int i=1; i<=n; i++) {
		scanf("%d",&a[i]);
		pos[a[i]]=i;
	}
	for(int i=1; i<=n; i++) {
		scanf("%d",&b[i]);
	}
	printf("%d\n",solve());
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值