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(n∗n) 的算法无法解决, 选用 O ( n l o g n ) O(nlogn) O(nlogn)算法解决.
DP算法 O ( n ∗ n ) O(n*n) O(n∗n)
-
定义 d p [ i ] dp[i] dp[i] 是以i为结尾的上升子序列.
-
状态转移一定从 d p [ 1 , i − 1 ] dp[1 , i-1] dp[1,i−1]+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(n∗logn)
-
我们维护一个记录升序序列长为 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;
}