1. 程式人生 > >POJ 3670 Eating Together 二分解法O(nlgn)和O(n)算法

POJ 3670 Eating Together 二分解法O(nlgn)和O(n)算法

n) 數據 代表性 tld 優化 inline ont 思想 子序列和

本題就是一題LIS(最長遞增子序列)的問題。本題要求求最長遞增子序列和最長遞減子序列。

dp的解法是O(n*n),這個應該大家都知道。只是本題應該超時了。

由於有O(nlgn)的解法。

可是因為本題的數據特殊性。故此本題能夠利用這個特殊性加速到O(n)的解法。當中的底層思想是counting sort分段的思想。就是假設你不會counting sort的話,就非常難想出這樣的優化的算法了。


O(nlgn)的利用單調隊列性質的解法,利用二分加速是有代表性的,無數據特殊的時候也能夠使用。故此這裏先給出這個算法代碼。

看了代碼就知道非常easy的了,只是這裏為了更加高效利用代碼,就使用了函數指針,代碼十分簡潔了,剛開始學習的人耐心點看,代碼應該非常好的:

#include <stdio.h>

const int MAX_N = 30000;
int arr1[MAX_N], arr2[MAX_N];
inline int max(int a, int b) { return a > b?

a : b; } inline bool larEqu(int a, int b) { return a <= b; } inline bool smaEqu(int a, int b) { return a >= b; } int biSearch(int low, int up, int val, bool (*func)(int , int)) { while (low <= up) { int mid = low + ((up-low)>>1); if (func(val, arr2[mid])) low = mid+1; else up = mid-1; } return low; } int getLIS(int n, bool (*func)(int, int)) { int j = 0; arr2[0] = arr1[0]; for (int i = 1; i < n; i++) { if (func(arr1[i], arr2[j])) arr2[++j] = arr1[i]; else arr2[biSearch(0, j, arr1[i], func)] = arr1[i]; } return j+1; } int main() { int n; while (scanf("%d", &n) != EOF) { for (int i = 0; i < n; i++) { scanf("%d", &arr1[i]); } printf("%d\n", n-max(getLIS(n, larEqu), getLIS(n, smaEqu))); } return 0; }



然後是O(n)的時間效率的算法。

就是由於僅僅有1,2,3,這三個數據,故此能夠分開窗體。分別記錄1。 2, 3 的數據段,在利用上面單調隊列的思想的時候,就能夠不使用二分法了,而是直接插入就能夠了,故此省去了lgn的時間。時間效率就優化到O(n)了。

這個算法卡了我的地方就是下標的問題,老是無法準確記錄窗體下標的,故此這裏使用個特殊的記錄下標的方法。看代碼就好像是個O(n*n)的算法。由於循環中有循環。可是大家細致看,事實上這是個O(n)算法。為什麽呢?由於循環中的循環總共僅僅是搜索了一遍n個數。無需反復搜索。


#include <stdio.h>

const int MAX_N = 30000;
int arr1[MAX_N], arr2[MAX_N];
inline int max(int a, int b) { return a > b? a : b; }

int getLIS(int n)
{
	int j = 0, one = 0, two = 0;
	arr2[0] = arr1[0];
	for (int i = 1; i < n; i++)
	{
		if (arr1[i] >= arr2[j])
		{
			arr2[++j] = arr1[i];
		}
		else
		{
			if (arr1[i] == 1)
			{
				while (arr2[one] < 2 && one < j) one++;
				arr2[one] = arr1[i];
			}
			else
			{
				while (arr2[two] < 3 && two < j) two++;
				arr2[two] = arr1[i];
			}
		}
	}
	return j+1;
}

int getLDS(int n)
{
	int j = 0, two = 0, thr = 0;
	arr2[0] = arr1[0];
	for (int i = 1; i < n; i++)
	{
		if (arr1[i] <= arr2[j]) arr2[++j] = arr1[i];
		else
		{
			if (arr1[i] == 3)
			{
				while (arr2[thr] > 2 && thr < j) thr++;
				arr2[thr] = arr1[i];
			}
			else
			{
				while (arr2[two] > 1 && two < j) two++;
				arr2[two] = arr1[i];
			}
		}
	}
	return j+1;
}

int main()
{
	int n;
	while (scanf("%d", &n) != EOF)
	{
		for (int i = 0; i < n; i++)
		{
			scanf("%d", &arr1[i]);
		}
		printf("%d\n", n-max(getLIS(n), getLDS(n)));
	}
	return 0;
}



POJ 3670 Eating Together 二分解法O(nlgn)和O(n)算法