Bridging signals(最长上升子序列)

本文探讨了在芯片设计中如何通过将最长上升子序列问题转化为计算机程序,寻找在不交叉条件下最大信号桥接数量。介绍了两种算法,包括O(n^2)的动态规划改进版和O(n log n)的贪心二分法,以降低复杂度应对大规模信号端口问题。
摘要由CSDN通过智能技术生成

Bridging signals

题目

“哦,不,他们又做到了”,Waferland 芯片厂的首席设计师喊道。布线设计人员再次完全搞砸了,使连接两个功能块端口的芯片上的信号到处都是相互交叉的。在这个过程的后期阶段,重做路由的成本太高了。相反,工程师必须使用第三维来桥接信号,以免两个信号交叉。然而,桥接是一个复杂的操作,因此希望桥接尽可能少的信号。迫切需要一种计算机程序,以找到可以在硅表面上连接而不相互交叉的最大数量的信号。记住在功能块的边界处可能有数千个信号端口,这个问题对程序员提出了很多要求。你能胜任这项任务吗?

图 1 示意性地描绘了一种典型情况。两个功能块的端口从 1 到 p,从上到下编号。信号映射由数字 1 到 p 以范围 1 到 p 中的 p 个唯一数字的列表的形式进行描述,其中第 i 个数字指定右侧的哪个端口应连接到i:左侧的第一个端口。当且仅当连接每对两个端口的直线交叉时,两个信号交叉。
在这里插入图片描述

思路

  • 将相交翻译为数学语言即 a [ i ] > a [ j ] a[i] > a[j] a[i]>a[j] 其中 a [ i ] a[i] a[i] 表示第 i i i 个位置的链接点编号.
    为不相交即保持 a [ i ] < a [ j ] ( i < j ) a[i]<a[j] (i<j) a[i]<a[j](i<j) ,所以很明显发现这是一个最长上升子序列问题(LIS)
  • 由于数据规模 40000 40000 40000 ,因此 O ( n ∗ n ) O(n*n) O(nn) 的算法无法解决, 选用 O ( n l o g n ) O(nlogn) O(nlogn)算法解决.

DP算法 O ( n ∗ n ) O(n*n) O(nn)

  • 定义 d p [ i ] dp[i] dp[i] 是以i为结尾的上升子序列.

  • 状态转移一定从 d p [ 1 , i − 1 ] dp[1 , i-1] dp[1,i1]+1的最大值

    即: d p [ i ] = m a x ( d p [ 1 , j ] ) + 1 dp[i] = max(dp[1,j])+1 dp[i]=max(dp[1,j])+1 ( a [ i ] > a [ j ] 且 j < i ) (a[i]>a[j]且j<i) (a[i]>a[j]j<i)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<fstream>
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define ull unsigned long long
#define endl '\n'
//#define int long long
using namespace std;
typedef pair<int,int> PII;
const int N =10 + 4e4 ,mod=1e9 + 7;
int dp[N];
int n;
int a[N];
void solve()
{    
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	memset(dp,0,sizeof dp);
	for(int i=2;i<=n;i++)// O(n^2)
	{
		dp[i] = 1;
		for(int j=i-1;j>=1;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;
}
signed main()
{
	ios::sync_with_stdio();cin.tie();cout.tie();

	int T;cin>>T;
	while(T--)
		solve();

	//cerr<<endl<<" Time : "<< T2-T1 <<"ms."<<endl; 
	return 0;
}

显然该算法无法解决本题.


贪心+二分 O ( n ∗ l o g n ) O(n*logn) O(nlogn)

  • 我们维护一个记录升序序列长为 i i i时最小末尾值的数组 l i s [ n ] lis[n] lis[n].
    这个数组的最大长度即为所求

  • 关于此算法的正确性不会很严谨的证明
    暂时感性认为,末尾越小越有利于之后构成最长上升序列.

  • 因为维护过程中总是在查找 a [ i ] a[i] a[i]的替换位置,该数列又是有序的,因此可以用二分优化.

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<fstream>
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define ull unsigned long long
#define endl '\n'
//#define int long long
using namespace std;
typedef pair<int,int> PII;
const int N =10 + 4e4 ,mod=1e9 + 7;
int dp[N];
int n;
int a[N];
void solve()
{    
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",a+i);
	vector<int>lis(n);
	{
		for(int i=0;i<n;i++)lis[i] = inf;
	}

	for(int i=1;i<=n;i++)
	{
		int p = lower_bound(lis.begin(),lis.end(),a[i]) - lis.begin();
		lis[p] = a[i];
	}
	printf("%d\n",lower_bound(lis.begin(),lis.end(),inf) - lis.begin());
}
signed main()
{
	// ios::sync_with_stdio();cin.tie();cout.tie();

	int T;cin>>T;
	while(T--)
		solve();

	//cerr<<endl<<" Time : "<< T2-T1 <<"ms."<<endl; 
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值