题目链接:http://poj.org/problem?id=1631
题目大意:给定1个序列,不会重复,x出现在y位置,即代表x与y可以连线。最后要从这个序列中尽量多地选择连线并保证连线不交叉。
解题思路:其实这题就是两个序列连线,求连线不交的最多连线数。本题一个序列已经固定,那只要保证另一列被选择的数是递增的就不会相交了,不理解的话画几个模拟下就懂了。那问题就转化为最长递增子序列了。一般的解法时间复杂度是n^2,但这题数据较大,必须要有更高效的解法。由于每次更新最长序列长度都是根据已有的长度,试想用一个数组来模拟这个过程,数组下标代表长度,数组内容代表当前长度最小的数值。这样更新答案只要从下标最大的那个地方开始比较就ok。由于长度是递增的,可以用二分查找来找每次更新的那个下标。
测试数据:
4
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原创,但可以转载,因为我们是兄弟。