1. 程式人生 > >Code is to be happy

Code is to be happy

1.瞭解動態規劃 

   寫的不是特別詳細,湊合看吧,嘿嘿, 快要考試了,攢人品....

動態規劃的核心思想就是避免子問題重複計算,採用用空間換取時間的方法提高演算法效率,比如用遞迴實現的斐波那契數與用陣列記錄子問題實現的遞推演算法就是最簡單的動態規劃思想,用表的形式記錄子問題,防止重複求解,類似的例子也使用與其他遞迴公式,難點在於最優子結構性質的發現與證明,通常找出最優子結構後都能寫出遞推表示式,從而寫出動態規劃的演算法.

動態規劃的解題方法:

如果儲存解的陣列是二維的,或可用二維陣列解決的,通常可以畫出二維矩陣,行座標與豎座標分別對應問題中實際意義.舉一個簡單的實際的帶資料的例子.按照最優子結構(如果還沒找到,就根據問題要求),自底向上的求解,最後通常能準確的寫出遞推關係

.

2.典型動態規劃例題

2.1 0-1揹包問題

 2.1-1.問題描述:

一共n中物品,每樣物品只有一件,物品i的重量為wi>0,價值為vi,揹包的最終容量為weight,求如何新增物品,在不超過揹包容量的情況下,揹包中物品的價值最大?

    分析:   這是一道典型的動態規劃題目,當然可以用回溯法列舉每一種可能,最後求出最大值,但是回溯法的搜尋空間為2^n,即每一種物品都有選和不選之分,選擇該物品為1,為右子樹,不選該物品為0,在為左子樹,這樣的一個搜尋空間為2^n,複雜度為O(2^n).  由於0-1揹包問題很顯然含有很多子問題,畫出回溯法時的解空間樹就可以知道有很多重疊的子樹,因此我們考慮用動態規劃方法求解.

2.1-2.動態規劃方法,找出最優子結構

    設Z={z1, z2, z3, ....,zn}是一個最優解,其中zi=1表示揹包中含有該物品,zi=0表示揹包中不含該物品.0-1揹包問題Knap(n, weight)在此時的價值為dp[n][weiht].下面為0-1揹包的最優子結構.

   (1)  如果zn=1,那麼dp[k-1][weight - w[n]] + v[n] > dp[n-1][weight],而且{z1,z2,…,zn-1}是Knap(n-1,weight- w[n])的最優解.(最優解包含子問題的最優解)
   (2)  如果zk=0,那麼dp[n-1][weight - w[n]) + v[n] <= dp[n-1][weight],而且{z1,z2,…,zn-1}是Knap(n-1,weight)的最優解。
   證明:
   (1)如果dp[k-1][weight - w[n]] + v[n] <= dp[n-1][weight],則有dp[n][weight]= dp[n-1][weight - w[n]] + v[n] <= dp[n-1][weight],從而得出Z={z1,z2,…,zn}不是最優解,與前提矛盾。因此dp[n-1][weight - w[n]] + v[n] > dp[n-1][weight]。假設{z1,z2,…,zn-1}不是Knap(n-1,weight- w[n])的最優解,則存在一個解使得f[n-1][weight] > dp[n-1][weight],則f[n-1][weight]+ v[n] > dp[n-1][weight - w[n]] + v[n] = dp[n][weight],與假設矛盾,所以{z1,z2,…,zn-1}是Knap(n-1,MaxWeight- w[n])的最優解。

  (2)  正法與1)類似。

    2.1-3.  0-1揹包問題的遞推式

dp[j] = max{dp[j], dp[j-w[i]]+v[i]}, 其中1=<i<=n, w[i]<=j<=weight. dp[j]表示揹包中重量為j時的最大價值

2.1-4. 虛擬碼

int Knap(int n)
{
	int i,j;
	for(i=0; i<=Weight; i++)//init dp
		dp[i] = 0;

	for(i=1; i<=n; i++)
	{
		for(j=Weight; j>=w[i]; j--)
		{
			//if select the ith goods,then dp[j] = v[i]+dp[j-w[i]] 
			dp[j] = max(dp[j], v[i] + dp[j-w[i]]);
		}
	}
	return dp[Weight];//return max value 
}


2.2 迴文串問題 

2.2-1.問題描述:

 給出一個字串,包含大寫字母,小寫字母和數字,例如Ab3bd,只有插入操作,一次只能新增一個字元,最終的目的是使其成為迴文串,即為對稱的字串,上例中的結果可以是Adb3bdA,此時新增2個字元,分別是第二個字元d和最後一個字元A,使其成為迴文串.

2.2-2. 動態規劃找出最優子結構:

si表示給出的字串的第i個字元,此問題的最優子結構可以描述為:若(si, ......, sj)為插入字元最少獲得的迴文串,則(s(i+1), ...., s(j-1))也必須為插入最少字元而獲得的迴文串.

    證明:假設(s(i+1), ...., s(j-1))不是插入最少字元而獲得的迴文串,那麼一定存在另一種插入方式(s'(i+1), ...... , s'(j-1))獲得到迴文串的插入次數少於(s(i+1), ...., s(j-1)),而此時與(si, ......, sj)為插入字元最少獲得的迴文串相矛盾,因此該問題具有最優子結構.

2.2-3.自頂向下的遞推公式

dp[i][j]表示第i個字元到第j個字元時的子字串(子問題)獲得迴文串需要的插入次數,

     dp[i][j] = dp[i+1][j-1],  當s[i]=s[j]時

     dp[i][j] = min(dp[i][j-1], dp[i+1][j])+1, 當s[i]!=s[j]時

2.2-4.虛擬碼

int DP(char a[],int n)
{
	int j;
	for(int k=2;k<=n;k++)
	{
		for(int i=1;i<n;i++)
		{
			j = k+i-1;
			if(a[i]==a[j])
				ans[i][j] = ans[i-1][j-1];
			else
				ans[i][j] = min(ans[i][j-1],ans[i+1][j])+1;
		}
	}
       return a[1][n];
}


   2.3 編輯距離問題

2.3-1.問題描述

   指兩個字串(X , Y)之間,字串X轉成字串Y所需的最少編輯操作次數,記為字串X到字串Y的最小編輯距離,記作dp[m][n](其中m,n分別表示字串X 和字串Y的長度).允許的編輯操作有替換,插入,刪除(此處和演算法導論課後的習題不同,減少了3個操作),在這裡假設所有操作的代價分別cost(replace), cost(insert),cost(delete).

例如:kityten -> sitting

 1.sityten (k->s)

 2.sitten (刪除y)

 3.sittin (e->i)

 4.sitting(插入g)

2.3-2.最優子結構

定義dp[i][j]表示表示字串X的長度為i的子串(X的第1個字元到第i個字元)到字串Y的長度為j的子串的編輯距離(即最小編輯代價).

分情況討論:

(1):最後一次操作為替換,若為替換則可知X[i]!=Y[j],因此替換操作後X[i]=Y[j],該問題就轉化為X[i-1]轉為Y[j-1]的問題,此時dp[i][j]=dp[i-1][j-1]+cost(repace);

最優子結構描述:如果最優一次操作為替換操作,那麼dp(i-1,j-1)是字串X長度為i-1的字串到字串Y長度為j-1的字串的編輯距離(最小編輯代價)

證明:反證法:若dp(i-1,j-1)不是字串X長度為i-1的字串到字串Y長度為j-1的字串的最小編輯代價,那麼存在另一種編輯方案,使得dp'(i-1,j-1)小於dp(i-1,j-1).這與前提矛盾,因此具有最優子結構.

(2):最後一次操作為插入操作,則字串X沒有發生改變,字串Y新增一個字元(第j個字元)因此該問題就轉化為X[i]轉為Y[j-1]的問題,此時dp[i][j] = dp[i][j-1]+cost(insert)

最優子結構及證明類似替換.

(3):若最後一次操作為刪除操作,則字串X將X[i]刪除,對字串Y沒有影響,此時該問題轉化為X[i-1]到Y[j]的問題,於是dp[i][j]=dp[i-1][j]+cost(delete)

最優子結構及證明類似替換.

2.3-3.遞推公式

   dp[i][j] = min(dp[i-1][j-1]+cost(replace), dp[i][j-1]+cost(insert), dp[i-1][j]+cost(delete) ).

2.3-4.虛擬碼:

int EditDistance(char *X, char *Y)
{
	m = strlen(X), n = strlen(Y);
	int **dp = new int[m][n];
	for(i=0; i<=m; i++)
	{
		dp[i][0] = i;
	}
	for(i=0; i<=n; i++)
	{
		dp[0][j] = j;
	}
	for(i=1; i<=m; i++)
	{
		for(j=1; j<=n; j++)
		{
			if(X[i] == Y[j])
			{
				dp[i][j] = dp[i-1][j-1] + cost(replace);
			}
			else
			{
				dp[i][j] = min(dp[i][j-1]+cost(insert), dp[i-1][j]+cost(delete));
			}
		}
	}
	return dp[m][n];
}