動態規劃——最長上升子序列(LIS)

最長上升子序列(LIS)

問題背景:
有一個數列,從其中挑選一些數字但保證他們之間的前後關係不變組成一個新數列,那麼稱這個新的數列是原數列的一個子數列。
最長上升子序列問題就是保證跳出來的子序列是嚴格遞增的,一般是求出這個子序列的長度。
這裏我先給出一種暴力搜索的方法(沒有任何優化):

#include<iostream>
#include<algorithm>
using namespace std;
int s, n, a[20], ans=0, f[100] = {0};
int dfs(int x)
{
	int s=0;
	for(int i = x+1;i <= n; i++)
	if(a[x] < a[i])
	s = max(s, dfs(i));
	s++;
	return s;
}
int main()
{
	cin >> n;
	for(int i = 1; i <= n; i++)
	cin >> a[i];
	for(int i = 1; i <= n; i++)
	ans = max (ans, dfs(i));
	cout << ans;
 } 

說明一下,這裏的int s=0很重要,每次使用的s必須是新的,不然會對前面s有影響,出現最長只有2的情況。
這種方法是記錄以每一個元素開頭的最長上升子序列
這種方法太過於複雜,在搜索時有很多重複的地方
比如:1 2 3 4 5
第一遍搜索時我們目的是要找到以1位開頭的最長上升子序列 就已經分別搜索過以1 2 3 4 5開頭的最長上升子序列
到了第二遍搜索時,我們要找的是以2開頭的最長上升子序列,這是在進行重複的操作。我們可以使用一種記憶化搜索的方法來記錄我們已經搜索過以某元素開頭的最長上升子序列。
把dfs函數改一下就可以得到:

#include<iostream>
#include<algorithm>
using namespace std;
int s, n, a[20], ans=0, f[100];
int dfs(int x)
{
	if(f[x] > 0)
	return f[x]; 
	for(int i = x + 1; i <= n; i++)
	if(a[x] < a[i])
	f[x] = max(f[x], dfs(i));
	f[x]++;
	return f[x];
}
int main()
{
	cin >> n;
	for(int i = 1; i <= n; i++)
	cin >> a[i];
	for(int i = 1; i <= n; i++)
	ans = max (ans, dfs(i));
	cout << ans;
 } 

f[x]就表示以第x個元素開頭的最長上升子序列,這樣就可以減少很多重複的操作。
下面介紹一下LIS的dp寫法(雖然記憶化搜索也是dp)
我們這樣定義f[i],f[i]表示以第i個元素爲末尾的最長上升子序列長度,這樣可以寫成狀態轉移方程:
If(a[i] < a[j] && i < j)
f[j] = max(f[j], f[i] + 1)

先給出代碼我們再分析:

#include<iostream>
#include<algorithm>
using namespace std;
int f[101], a[101];
int main()
{
	int n, ans = 0;
	cin >> n;
	for(int i = 1; i <= n; i++)
	cin >> a[i];
	for(int i = 1; i <= n; i++)
	{
		f[i] = 1;
		for(int j = 1; j < i; j++)
		{
			if(a[j] < a[i])
			f[i] = max(f[i], f[j] + 1);
		}
		ans = max(ans, f[i]);
	}
	cout <<"最長上升子序列長度:"<< ans;
	return 0;
 } 

這樣說可能比較抽象,我們用具體的數據來分析一下:
比如一個數列 9 7 3 2 5 8 7 10
i = 1
f[1] = 1不用說
i = 2
因爲9 > 7所以以第二個數7爲終點的最長上升子序列是1
f[2] = 1
i = 3
與I = 2的情況是一樣的

i = 5
進入轉移狀態
f[5] = max(f[5], f[4] +1) = max(1, 2) = 2

i = 7
先是j=3,4進入轉移狀態
f[7] = max(f[7], f[3,4] + 1 ) = 2
然後到j=5又進入轉移狀態
f[7] = max(f[7], f[5] + 1) = max(1, 2 + 1) = 3
這樣是不是與記憶化搜索有點像,記錄了f[5]供f[7]用。
這裏的ans就是找出f[i]中最大的,即最長上升子序列,這樣是不是很好理解了。
在這裏我給出每一個f[i]的值:
第二行是輸入的數據,第三行是對應以該節點爲終點的最大上升子序列長度
第二行是輸入的數據,第三行是對應以該節點爲終點的最大上升子序列長度
注:此文章問原創,開源,可轉載但請註明出處!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章