動態規劃
一、動態規劃算法的思想
動態規劃算法的基本思想是將待求解的問題問題分解成若干子問題,先求解子問題,然後從這些子問題的解得到原問題的解。與分治法不同的是,分解得到的子問題往往不是相互獨立的,計算時先保存已解決的子問題的答案,再自底向上計算出待求解的問題。
二、矩陣連乘問題
首先了解:①A是p x q矩陣,B是q x r矩陣,C=A x B是p x r矩陣,A x B需要pqr次乘法計算;②矩陣相乘滿足結合律即 (AB)C=A(BC) ,多個矩陣相乘時不同的結合方式會有不同的乘法計算次數,通過不同的加括號方式找到最少的乘法計算次數即爲要解決的問題。
#define MAX 100
int m[MAX][MAX] = {0}; //保存乘法計算次數的數組 ,可通過主函數輸出
int s[MAX][MAX] = {0}; //保存斷點(即加括號位置)的數組 ,可通過主函數輸出
int p[MAX+1]; //保存矩陣行列值的數組 ,通過主函數數日
int n; //連續相乘的矩陣個數 ,通過主函數輸入
void MatixChain(int m[][MAX],int s[][MAX],int p[],int n)
{
int i,j,r,k,t;
for(i=1;i<=n;i++) m[i][i] = 0;
for(r=2;r<=n;r++) //設置 j比i大 r-1
{
for(i=1;i<=n-r+1;i++)
{ //j-i=r-1 ,例如r=2時可計算m[1] [2],m[2][3]...,r=3時計算m[1][3],m[2][4],m[3][5],r=4時計算m[1][4],m[2][5]
j=i+r-1;
m[i][j]=m[i+1][j]+p[i-1]*p[i]*p[j]; //首先計算斷點爲 i 時,矩陣連乘次數 ,用於下面的比較
s[i][j]=i; // 斷點爲 i
for(k=i+1;k<j;k++) //斷點從 k=i+1 開始,直到斷點爲 k=j-1
{
t=m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j]; //計算每一個斷點下矩陣連乘次數
if(t<m[i][j]) //記錄連乘次數少的次數及斷點位置
{
m[i][j]=t;
s[i][j]=k;
}
}
}
}
}
三、最長公共子序列問題
例如序列X:A B C B D A B;序列Y:B D C A B A。則X和Y的最長公共子序列爲:B D A B。
注意:最長公共子序列並不需要在原序列中是連續的。
#define MAX 100
char X[MAX]; //序列 X
char Y[MAX]; //序列 Y
int b[MAX][MAX]; //記錄得到最長公共子序列的每一步的獲得方式
int sx,sy; //X、Y 序列的長度
int C[MAX][MAX]={0}; //記錄不同長度的 X和Y 序列,其最長公共子序列的長度
void LSCLength(char X[],char Y[],int b[][MAX],int sx,int sy,int C[][MAX])
{
int i,j,t;
for(i=1;i<=sx;i++) C[i][0]=0; //一個序列長度爲0時,最長公共子序列長度爲0
for(j=1;j<=sy;j++) C[0][j]=0; //同上
for(i=1;i<=sx;i++) //i,j 均從1 開始,體現了dp思想的先求解小問題並保存其解,自底向上原問題的解
{
for(j=1;j<=sy;j++)
{
if(X[i]==Y[j]) //序列的最後一個元素相同時 ,
{
C[i][j]=C[i-1][j-1]+1; //公共子序列長度至少爲 1 ,且等於兩序列去掉最後一個元素後的最長公共子序列長度 + 1
b[i][j]=1; //此方式下得到公共子序列爲方式 1
}
else
{
if(C[i-1][j]>=C[i][j-1]) //兩序列的最後一個元素不相等時,取 max(第一個序列去掉最後一個元素和第二個序列的LSCL,第二個序列去掉最後一個元素和第一個序列的LSCL)
{
C[i][j]=C[i-1][j];
b[i][j]=2; //第一個序列去掉最後一個元素和第二個序列的LSCL最大時爲方式 2
}
else
{
C[i][j]=C[i][j-1]; //第二個序列去掉最後一個元素和第一個序列的LSCL最大時爲方式 3
b[i][j]=3;
}
}
}
}
}
三、最大子段和
例如:序列:[-2,11,-4,13,-5,-2] 最大字段和爲20,即[11,-4,13]相加。
注意:最大子段和所對應的序列是連續的。
#define MAX 1000
int b[MAX];
int s[MAX]; //輸入的序列
int len; //輸入序列的長度
int MaxSum(int b[],int s[],int len)
{
int i;
int sum=0;
b[0]=0; //每次計算都需初始化b[0]
for(i=1;i<=len;i++)
{
if(b[i-1]>0) //如果b[i-1]>0,則說明再往下加有可能得到更大的值,同樣也有可能得到的值變小
//只需將執行每步 後所得的值與之前得到的sum相比較,變大則保存在sum中,變小則不保存
{
b[i]=b[i-1]+s[i]; //當前b[i-1]大於0,期望得到更大的值
}
else b[i]=s[i]; //不料b越加越小,已小於0,遇到一個值就保存一個,以期遇到大於0的值
if(b[i]>sum) sum=b[i]; //保存每步執行後的目前得到的最大值
}
return sum;
}
四、未完待更…