Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 12333 | Accepted: 6736 |
Description
A typical situation is schematically depicted in figure 1. The ports of the two functional blocks are numbered from 1 to p, from top to bottom. The signal mapping is described by a permutation of the numbers 1 to p in the form of a list of p unique numbers in the range 1 to p, in which the i:th number specifies which port on the right side should be connected to the i:th port on the left side.Two signals cross if and only if the straight lines connecting the two ports of each pair do.
Input
Output
Sample Input
4 6 4 2 6 3 1 5 10 2 3 4 5 6 7 8 9 10 1 8 8 7 6 5 4 3 2 1 9 5 8 9 2 3 1 7 4 6
Sample Output
3 9 1 4
题目大意:如图,每个左边对应一个右边的值,求不交叉最多能连上多少条线。
大致思路:最长下降子序列。用dp[i表示a[i]结尾个最长连续的长度。]转移方程:dp[i]=max(dp[j]+1,dp[i]) (1<=j<i) 。结果就光荣的超时了吧。数据有10^4的那么大,O(n2)的复杂度做怪不得会超时了。看了一下大家的解法,是可以用nlogn的复杂度解决最长下降(上升)子序列的问题。具体怎么做呢?用二分和单调队列来优化dp,就是增加一个q[]数组模拟队列,q[i]=k表示最长下降子序列长度为i的情况下最大数字为k,依次用原数组来替换这个q数组,这个q数组是单调下降的,可以打个表或者网上有详细证明,这样就能用二分把复杂度降到nlogn。具体模拟过程这里写的很好理解:http://www.docin.com/p-540470161.html
以下代码:
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int num[50000],dp[50000],q[50000]; //dp[i]是以a[i]结尾最长下降子序列的长度,改进之后暂时不用可以打出来辅助理解
int main(){
ios::sync_with_stdio(false);
int t;
scanf("%d",&t);
while(t--){
memset(q,0,sizeof(q));
memset(dp,0,sizeof(dp));
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&num[i]);
}
int len=1;
q[1]=num[1];
// dp[1]=1;
for(int i=2;i<=n;i++){
int l=1,r=len;
while(l<=r){
int mid=(l+r)>>1;
if(q[mid]<num[i])
l=mid+1;
else
r=mid-1;
}
q[l]=num[i]; //每次把原数组按大小情况入队
// dp[i]=l;
len=max(len,l); //更新最长序列的长度(在上面插入了更小的数的情况下)
}
// for(int i=1;i<=n;i++) cout<<dp[i]<<' ';cout<<endl;
// for(int i=1;i<=len;i++) cout<<q[i]<<' ';cout<<endl;
printf("%d\n",len);
}
return 0;
}