poj 1631 Bridging signals (DP_最长递增子序列nlogn)

题目链接: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 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原创,但可以转载,因为我们是兄弟。

你可能感兴趣的:(测试)