题目链接:http://poj.org/problem?id=1631
题目大意:给定1个序列,不会重复,x出现在y位置,即代表x与y可以连线。最后要从这个序列中尽量多地选择连线并保证连线不交叉。
解题思路:其实这题就是两个序列连线,求连线不交的最多连线数。本题一个序列已经固定,那只要保证另一列被选择的数是递增的就不会相交了,不理解的话画几个模拟下就懂了。那问题就转化为最长递增子序列了。一般的解法时间复杂度是n^2,但这题数据较大,必须要有更高效的解法。由于每次更新最长序列长度都是根据已有的长度,试想用一个数组来模拟这个过程,数组下标代表长度,数组内容代表当前长度最小的数值。这样更新答案只要从下标最大的那个地方开始比较就ok。由于长度是递增的,可以用二分查找来找每次更新的那个下标。
测试数据:
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
代码:
#include <stdio.h>
#include <string.h>
#define MAX 40010
#define max(a,b) (a) > (b) ? (a) : (b)
int n,arr[MAX];
int ans,maxin[MAX];
void Solve() {
int i,j,k,tp;
int low,high,mid;
memset(maxin,'255',sizeof(maxin));
maxin[ans] = arr[1]; //初始化,长度为1的时候是arr[1]最小
for (i = 2; i <= n; ++i) {
low = 1,high = ans;
while (low <= high) {
mid = low + (high - low) / 2;
if (maxin[mid] < arr[i]) low = mid + 1;
else if (maxin[mid] > arr[i]) high = mid - 1;
}
if (maxin[high+1] > arr[i]) {
maxin[high+1] = arr[i];
ans = max(high+1,ans);
}
}
}
int main()
{
int i,j,k,t;
scanf("%d",&t);
while (t--) {
scanf("%d",&n);
for (i = 1; i <= n; ++i)
scanf("%d",&arr[i]);
ans = 1;
Solve();
printf("ans = %d\n",ans);
}
}
本文ZeroClock原创,但可以转载,因为我们是兄弟。