1. 程式人生 > >動態規劃經典最優子結構總結

動態規劃經典最優子結構總結

(1)0-1揹包:

如果有一個揹包,總載重量為Wt,有n個物品,每個物品重Wi,每個物品價值Ci,那麼如何裝才能讓這個包裡面東西的價值最高?

dp[i][j]為前i個物品當中選擇裝進一個載重量為j的包裡最大價值,則:

初始化:dp【0】【x】=0;for i=0-》n;j=0-》Wt

dp[i][j]的值為:dp[i-1][j](第i個物品不裝進包裡),dp[i-1][j-Wi]+Ci(第i個物品裝進包裡)的較大者。

(2)有代價的最短路徑(動態規劃和dijkstra混合使用):

尋求點1到點A之間的最短路,但是每到一個點就會消耗一點錢,這個值用money【i】來表示,錢不夠路不通,初始有錢N。

dp【i】【j】為走到第i個點,還剩j錢的最短路徑,則:

初始化dp【x】【y】=inf;dp【1】【N-money[1]】=0;ifvisited[x][y]=false;ifVisited[1][N-money[1]]=true;

shortest=inf;
moneyLeft=inf;
number=inf;
while(1){
	for(int z=0;z<A;z++)
	for(int x=0;x<N;x++){
		if(dp[z][x]<number){
			shortest=z;
			moneyLeft=x;
			number=dp[z][x];
		}
	}
	if(shortest==inf)break;
	for(int i=0;i<N;i++){
	if(ifVisited[i]==false&&g[shortest][i]!=0){
		if(moneyLeft>=money[i]&&g[shortest][i]+dp[shortest][moneyLeft]>dp[i][moneyLeft-money[i]]){
			dp[i][monetLeft-money[i]]=dp[shortest][moneyLeft]+g[shortest][i];
		}
	 }
   }
}
result=max(dp[A][x]);


(3)劃分DP:

一個m位數,將其分成n部分,要求這n部分乘積最大,求分法。

規定a【i】【j】代表從第i位到j位這個數

並且,dp【i】【j】代表前i個數,分成k部分的最大積

那麼,dp【i】【j】=dp【x】【k-1】*a【x+1】【j】的最大值,x從0到j-1

初始化的時候,需要把dp【x】【1】初始化為a【1】【x】

__int64 number;
//初始化a陣列 
for(int i=1;i<m;i++){
	a[i][i]=number%10;
	number/=10;
} 
for(int i=1;i<m;i++){
	for(int j=i+1;j<=m;j++){
		a[i][j]=a[i][j-1]*10+a[j][j];
	}
}
//進行規劃
初始化dp後: 
for(int j=1;j<=n;j++){
	for(int i=j;i<=m;i++){
		for(int k=1;k<=i;k++){
			dp[i][j]=max(dp[i][j],dp[k][j-1]*a[k+1][j]);
		}
	}
} 


(4)湊數

假設有幾種整數:1,2,4。。。。。。請你用這些數湊一個整數N,要求用的數的數量最少,每種數可以多次使用

dp【i】代表湊數字i所需要用到的最小數量,那麼

dp【i】=dp【i-1】到dp【i-2】到dp【i-4】的最小值加1.

(5)裝配線排程。

有兩個裝配線,1號,2號,每個線有N個裝配流程,一個產品要生產,必須走完這N個流程,但是每個流程要在1號或2號線裝配是都可以的,每個流程所需時間為Cij,產品每進入一個其他的裝配線(一開始不在裝配線上,進入也算進入其他的裝配線),需要消耗的時間為TIMEij,i代表第i條線,j代表第j個流程,求走完全部裝配線最小時間。

dp【i】【j】代表在第i條線完成第j個流程所需最小時間。

則dp【1】【j】=min(dp【1】【j-1】+C1j,dp【2】【j-1】+TIME1j+C1j)

(6)序列S和T之間的距離定義:刪除,修改,增加一個字元距離+1,最小的距離就是ST之間的距離,求ST之間最小距離:

dp【i】【j】代表S前i個T前j個的最小距離

則dp【i】【j】=min(dp【i-1】【j-1】+(s[i]==t[j])?0:1,dp【i-1】【j】+1,dp【i】【j-1】+1)

(7)最長公共子序列

當i==j dp【i】【j】=dp【i-1】【j-1】+1

如果i!=j,則dp【i】【j】=max(dp【i】【j-1】,dp【i-1】【j】)

回溯法求子序列:

while(unfinished){

if(s[i]=t[j]) push(s[i]); i--;j--;

else if(dp[i-1][j]大) i--,否則j--;

}

(8)最大子序列

dp【i】=(dp【i-1】>0?)dp【i-1】+string【i】:string【i】

(9)最長遞增子序列

dp【k】代表前k個數字的最長,包含k

如果number【k】>number【k-1】,則dp【k】=dp【k+1】+number【k】

否則,向前尋找到第倒數第一個比k小的,比如是第A個,則dp【k】=max(dp【A】+number【k】)