动态规划看这一篇就够了!

算法设计与分析----动态规划(持续更新中…)

一,矩阵链相乘问题:

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:

扫描二维码关注公众号,回复: 12787456 查看本文章
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);

猜你喜欢

转载自blog.csdn.net/timelessx_x/article/details/114680464
今日推荐