1. 程式人生 > >12.29 洛谷1020 導彈攔截(尋找最長遞減/增數列)

12.29 洛谷1020 導彈攔截(尋找最長遞減/增數列)

越往後學,你就越會知道,數學不好帶來的苦痛與折磨。
這第二題要是不會數學沒個做啊(大哭
第一問我本來想用動態規劃做,但是感覺會爆。。畢竟六位數,n^2的時間複雜度。有人想用a[i][j]表示從i到j的遞減數長度最大值,最後再逐次比較a[i][j]的大小……
哈哈哈,應該可行。但是還是nlogn的時間複雜度比較好啊。直接遞減數列找然後二分的方法!簡單而容易理解!!!耗時也少!妙哉,妙哉。
第二問等價於求一個最長遞增數列。有人給出了證明,但我沒怎麼看懂,潸然淚下啊。

#include<iostream>
#include<memory.h>
#include<cstdio>
using namespace std;
int main()
{
	int a[100005];  int i=0,mid,r;
	while (scanf("%d", &a[i++]) != EOF)continue;
	i--;
	int f[100000] = { 1000000 };//f[0]得取一個大一點的數,不然可能比a[j]小。。就迴圈不了啦。
	int ans = 0, ans2 = 0;
	for (int j = 0; j <= i; j++)
	{
		if (f[ans] >= a[j])
		{
			f[++ans] = a[j];     //遞減數列的建立過程
		}
		else
		{
			int p = 1; r = ans;
			while (p < r) {
				mid = (p + r) / 2;
				if (f[mid] >= a[i])p = mid + 1;
				else {
					r = mid;
				}
			}
			if (p != 1)f[p] = a[i];
		}                                   //二分查詢新的數在遞減數列中的位置,並替換原來的數
	}                    
	cout << ans << endl;
	memset(f, 0, sizeof(f));    //這裡我本來想開一個新陣列g[100001]的,奈何vs報錯,估計是空間不夠了
	for (int j = 0; j <= i; j++)
	{
		if (f[ans2] <= a[j])
		{
			f[++ans2] = a[j];
		}
		else
		{
			int p = 1; r = ans2;
			while (p < r) {
				mid = (p + r) / 2;
				if (f[mid] < a[i])p = mid + 1;
				else {
					r = mid;
				}
			}
			f[p] = a[i];
		}
	}
	
	cout << ans2;
	system("pause");
	return 0;

}

ok,寫完了,我複習(yuxi)微積分去了。
1.2考試,菜雞落淚