實驗六( 動態規劃演算法)
一、實驗目的:
1.通過動態規劃演算法的示例程式理解動態規劃演算法的基本思想;
2.運用動態規劃演算法解決實際問題加深對動態規劃演算法的理解和運用;
二、實驗環境:
Visual Studio 2013
三、實驗內容:
(寫出主要的內容)
1. 分析並掌握“最長公共子序列” 問題的動態規劃演算法求解方法;
最長公共子序列問題:若給定序列X={x1,x2,…,xm},則另一序列Z={z1,z2,…,zk},是X的子序列是指存在一個嚴格遞增下標序列{i1,i2,…,ik}使得對於所有j=1,2,…,k有:zj=xij。例如,序列Z={B,C,D,B}是序列X={A,B,C,B,D,A,B}的子序列,相應的遞增下標序列為{2,3,5,7}。
給定2個序列X={x1,x2,…,xm}和Y={y1,y2,…,yn},找出X和Y的最長公共子序列。
在參考程式的基礎上,編寫主函式,實現最長公共子序列的動態規劃求解。測試資料包括教材155頁演算法設計題中給出的資料,另外再自己設計兩組測試資料。
程式碼:
#include<stdio.h>
#include<string.h>
#include <stdlib.h>
void LCSLength(char *x, char *y, int m, int n, int c[][100], int b[][100])
{
int i, j;
for (i = 1; i <= m; i++) c[i][0] = 0;
for (i = 1; i <= n; i++) c[0][i] = 0;
for (i = 1; i <= m; i++)
for (j = 1; j <= n; j++)
{
if (x[i] == y[j])
{
c[i][j] = c[i - 1][j - 1] + 1;
b[i][j] = 1;
}
else if (c[i - 1][j] >= c[i][j - 1])
{
c[i][j] = c[i - 1][j];
b[i][j] = 2;
}
else
{
c[i][j] = c[i][j - 1];
b[i][j] = 3;
}
}
}
void LCS(int i, int j, char *x, int b[][100])
{
if (i == 0 || j == 0) return;
if (b[i][j] == 1)
{
LCS(i - 1, j - 1, x, b);
printf("%c", x[i]);
}
else if (b[i][j] == 2)
LCS(i - 1, j, x, b);
else LCS(i, j - 1, x, b);
}
int main()
{
int m, n,c[100][100],b[100][100];
char x[10001], y[10001];
scanf("%s", x);
scanf("%s", y);
m = strlen(x);
n = strlen(y);;
LCSLength(x, y, m, n, c, b);
LCS(m, n, x, b);
return 0;
}
2. 計算矩陣連乘積
在科學計算中經常要計算矩陣的乘積。矩陣A和B可乘的條件是矩陣A的列數等於矩陣B的行數。若A是一個p×q的矩陣,B是一個q×r的矩陣,則其乘積C=AB是一個p×r的矩陣。由該公式知計算C=AB總共需要pqr次的數乘。其標準計算公式為:
現在的問題是,給定n個矩陣{A1,A2,…,An}。其中Ai與Ai+1是可乘的,i=1,2,…,n-1。要求計算出這n個矩陣的連乘積A1A2…An,最少的乘法次數。
遞迴公式:
請編寫程式實現矩陣連乘問題的動態規劃演算法,自己設計不少於3組的測試資料,要求顯示出最少的乘法次數,以及最優計算次序。
程式碼:
#include <iostream>
using namespace std;
void MatrixChain(int *p,int n,int m[][100],int s[][100])
{
for (int i = 1; i <= n; i++)
m[i][i] = 0;
for (int r = 2; r <= n; r++)
for (int i = 1; i <= n - r + 1; i++) {
int j = i + r - 1;
m[i][j] = m[i + 1][j] + p[i - 1] * p[i] * p[j];
s[i][j] = i;
for (int k = i + 1; k < j; k++) {
int 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; }
}
}
}
void traceback(int i, int j, int s[][100])
{
if (i == j)
cout << "A" << i;
else if (i == j - 1)
cout << "(A" << i << "A" << j << ")";
else
{
cout << "(";
traceback(i, s[i][j], s);
traceback(s[i][j] + 1, j, s);
cout << ")";
}
}
int main()
{
int p[100], n, m[100][100], s[100][100];
cin >> n ;
for (int i = 0; i <= n; i++)
cin >> p[i];
MatrixChain(p, n, m, s);
traceback(1, n, s);
return 0;
}
3.最大子段和
給定由n個整數(可能有負整數)組成的序列(a1,a2,…,an),最大子段和問題要求該序列形如的最大值(1<=i<=j<=n),當序列中所有整數均為負整數時,其最大子段和為0。
1)用分治法求解。(選做)
2)用動態規劃法求解。
程式碼:
#include <iostream>
using namespace std;
int main()
{
int i,n, a[100], sum = 0, b = 0,start=0,end=0;
cin >> n;
for (i = 0; i < n; i++)
cin>>a[i] ;
for (i = 0; i < n; i++) {
if (b > 0){
if (start == 0)
start = i;
else
end = i;
b += a[i];
}
else b = a[i];
if (b > sum) sum = b;
}
cout << sum << endl;
return 0;
}
4.最大k乘積問題。
問題描述:設X是一個n位十進位制整數,如果將X劃分為K段,則可得到K個整數,這K個整數的乘積稱為X的一個K乘積。請設計演算法並程式設計實現,對於給定的X 和K,求出X的最大K乘積。
輸入:X,K,n
輸出:X的最大K乘積。
程式碼:
#include <iostream>
using namespace std;
int MAX=0;
void Max(int X, int K, int n,int m) {
int w=1;
if(K==0&&X!=0)
return ;
if(X==0&&MAX<m){
MAX=m;
return ;
}
for(int i=1;i<=n;i++){
w*=10;
if(K-1>n-i)
return ;
else
Max(X/w,K-1,n-i,m*(X%w));
}
}
int main()
{
int w=1,X,K,n;
cin >> X>>K>>n;
Max(X,K,n,1);
cout<<MAX<<endl;
return 0;
}