算法设计与分析----动态规划(持续更新中…)
一,矩阵链相乘问题:
1.问题引入:
矩阵相乘,计算顺序的不同可能会引起计算复杂度成百倍的增长,所以,求解矩阵链最优计算顺序就变得十分必要了!
输入说明:
第一行先输入一个正整数n(n<50);
接着输入n+1个正整数;(均不大于50)
输出说明:
输出一个正整数;
input case:
6
30 35 15 5 10 20 25
ouput case:
15125
2.code:
#include<stdio.h>
#include<iostream>
using namespace std;
#define INF 9999
int m[INF][INF];//用于存放各个规模问题的求解数
int s[INF][INF];//用于记录各个问题的划分(其实就是相乘时括号的界定)
void MAT(int n,int m[INF][INF],int s[INF][INF],int *p)//p就是这一组矩阵的行数,列数
{
for(int i=1;i<=n;i++){
//最小规初始化
m[i][i]=0;
}
for(int r=2;r<=n;r++){
//r用于控制这问题的规模,r(2->n)
for(int i=1;i<=n-r+1;i++){
//i用于控制问题的起始
int min=m[i][i]+m[i+1][i+r-1]+p[i-1]*p[i]*p[i+r-1];
s[i][i+r-1]=i;
m[i][i+r-1]=min;
for(int k=i+1;k<=i+r-1;k++){
//k就是当前子问题再划分
if(m[i][k]+m[k+1][i+r-1]+p[i-1]*p[k]*p[i+r-1]<min){
//找到更小的,更优的就换
min=m[i][k]+m[k+1][i+r-1]+p[i-1]*p[k]*p[i+r-1];
m[i][i+r-1]=min;
s[i][i+r-1]=k;
}
}
}
}
}
int main()
{
int n;
cin>>n;
int p[n+1];
for(int i=0;i<=n;i++){
//n个矩阵,对应数组应该为n+1
cin>>p[i];
}
MAT(n,m,s,p);
printf("%d\n",m[1][n]);
return 0;
}
3.复杂度分析:
时间复杂度:O(n^3)
空间复杂度:O(n^2)
看似时间复杂度很高,其实已经很优了(相比蛮力而言);
蛮力算法时间复杂度>=O(2^n),是指数级别的!;
二,最长公共子序列问题
1.问题引入:序列X={A,B,C,B,D,A,B};
序列Y={B,D,C,A,B,A};
则二者最长公共子序列长度为4;
可以为ans1={B,D,A,B},ans2={B,C,B,A};
输入说明:
第一行输入两个正整数m,n;
第二行输入m个字符;
第三行输入n个字符;
输出说明:
输出最长公共子序列长度;
输出最长公共子序列(答案不惟一)
input case:(不要从这里直接赋值样例,输入到终端就有问题了,我也不知道为啥会这样?)
7 6
A B C B D A B
B D C A B A
ouput case:
4
BDAB
2.code:
#include<stdio.h>
#include<iostream>
#include<malloc.h>
using namespace std;
#define INF 999
int c[INF][INF];//c用于存储i-j问题的的最长公共子序列长度;
int b[INF][INF];//b用于存储到底是1,2,3那种拆分形式;
void LCSLength(int c[INF][INF],int b[INF][INF],char *X,char *Y,int m,int n)//求解最长子序列长度与拆分形式函数
{
for(int i=1;i<=m;i++){
//只要i是零,最长公共子序列长度为0;
c[i][0]=0;
}
for(int i=1;i<=n;i++){
//同上
c[0][i]=0;
}
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
if(X[i]==Y[j]){
//如果有边界相同,形式1
b[i][j]=1;
c[i][j]=c[i-1][j-1]+1;
}else if(c[i-1][j]>c[i][j-1]){
//缺左边大,形式2
b[i][j]=2;
c[i][j]=c[i-1][j];
}else{
//缺右边大,形式3
b[i][j]=3;
c[i][j]=c[i][j-1];
}
}
}
}
void Print_Queue(int b[INF][INF],char *X,int m,int n)//打印函数
{
if(m==0||n==0)return ;
else if(b[m][n]==1){
//形式1
Print_Queue(b,X,m-1,n-1);
cout<<X[m];
}else if(b[m][n]==2){
//形式2
Print_Queue(b,X,m-1,n);
}else{
//形式3
Print_Queue(b,X,m,n-1);
}
}
int main()
{
int m,n;
scanf("%d %d",&m,&n);
getchar();
char *X=(char*)malloc(sizeof(char)*(m+1));
char *Y=(char*)malloc(sizeof(char)*(n+1));
for(int i=1;i<=m;i++){
scanf("%c",&X[i]);
getchar();
}
for(int i=1;i<=n;i++){
scanf("%c",&Y[i]);
getchar();
}
LCSLength(c,b,X,Y,m,n);
printf("%d\n",c[m][n]);
Print_Queue(b,X,m,n);
return 0;
}
3.复杂度分析:
LCSLength函数复杂度O(m*n);
Print_Queue函数复杂度O(m+n);
相较于蛮力算法:O(2^m)(指数级别)已经很优了!
3.最大子列和问题
**1.问题引入:**比如给定一个序列:(-2,11,-4,13,-5,-2);求其最大子列和,最终答案是从第二项加到第四项;最大子列和为20,那么如何使用动态规划思想呢?
input_case:
6
-2 11 -4 13 -5 -2
ouput_case:
best_i=2
best_j=4
maxsum=20
2.code:
#include<stdio.h>
//此处二值设为全局变量
int best_i=0;//best_i为最佳起始
int best_j=0;//best_j为最佳结尾
int getMaxSum(int *a,int n)
{
int b=0;
int sum=0;
for(int i=1;i<=n;i++){
if(b>0){
b+=a[i];
}else{
//找到起始就将best_i赋值为i
best_i=i;
b=a[i];
}
if(b>sum){
//找到合适的结尾就将best_j赋值为j
best_j=i;
sum=b;
}
}
return sum;
}
int main()
{
int n;
scanf("%d",&n);
int a[n+1];
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
int maxsum=getMaxSum(a,n);
printf("best_i=%d\n",best_i);
printf("best_j=%d\n",best_j);
printf("maxsum=%d\n",maxsum);
return 0;
}
3.时间复杂度分析:
时间复杂度明显为O(n),相较于最简单的做法(蛮力算法)O(n^3)有较为明显的差距;
4.最大子列和问题的扩展:矩阵的最大子矩阵和问题;
1.问题导入:
一个矩阵,m行n列,求最大的子矩阵的和问题;
input_case:
3 2
11 -5
22 44
-5 6
output_case:
maxsum=66
2.code:
#include<stdio.h>
#define INF 99
int M[INF][INF];//矩阵用二维数组存储,设全局变量
int getMaxSum(int *a,int n)//一维最大子列和问题函数,这个函数同问题3
{
int b=0;
int sum=0;
for(int i=1;i<=n;i++){
if(b>0){
b+=a[i];
}else{
b=a[i];
}
if(b>sum){
sum=b;
}
}
return sum;
}
int getMaxSum2(int m,int n,int M[INF][INF]){
//求最大子矩阵的和
int sum=0;
int *b=new int[n+1];
for(int i=1;i<=m;i++){
for(int k=1;k<=n;k++){
b[k]=0;
}
for(int j=1;j<=m;j++){
for(int k=1;k<=n;k++){
b[k]=M[j][k];
}
int max=getMaxSum(b,n);
if(max>sum){
sum=max;
}
}
}
return sum;
}
int main()
{
int m,n;
scanf("%d %d",&m,&n);
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
scanf("%d",&M[i][j]);
}
}
int maxsum=getMaxSum2(m,n,M);
printf("maxsum=%d\n",maxsum);
return 0;
}
3.复杂度分析:
由于问题三时间复杂度为O(n),这里的时间复杂度为O(nmm);