实验三 动态规划实验
- 第1关:编程实现矩阵连乘问题的求解
- 第2关:编程实现最大子段和问题的求解(分别采用分治法和动态规划法求解)
- 第3关:0-1背包
- 第4关:最长单调子序列
- 第5关:最长公共子序列(LCS)
第1关:编程实现矩阵连乘问题的求解
题目描述:
在计算矩阵连乘积时,加括号的方式对计算量有影响。
例如: 有三个矩阵A1,A2,A3连乘,它们的维数分别为
10100,1005,550。用第一种加括号方式(A1A2)A3计算,则所需数乘次数为101005+10550=7500。用第二种加括号方式A1(A2A3)计算,需要100550+10100*50=75000次数乘。
输入连乘矩阵的个数,每个矩阵的维数。要求输出最少数乘次数。
相关知识
输入格式
第一行输入一个n,代表有n个矩阵
接下来n行,每行输入两个数a,b,代表每个矩阵的维度。
0
输出格式
输出一个数,代表最小数乘次数。
测试输入:
6
30 35
35 15
15 5
5 10
10 20
20 25
预期输出:
15125
代码:
#include
int main(){
int n;
scanf("%d",&n);
int a[n][2];
int b[n][n]={
0};
for(int i=0;i<n;i++){
scanf("%d %d",&a[i][0],&a[i][1]);
}
for(int i=1;i<n;i++){
for(int j=0;j<n-i;j++){
b[j][j+i]=b[j][j]+b[j+1][j+i]+a[j][0]*a[j][1]*a[j+i][1];
int k=j+1;
for(;k<j+i;k++){
int t=b[j][k]+b[k+1][j+i]+a[j][0]*a[k][1]*a[j+i][1];
if(t<b[j][j+i]) {
b[j][j+i]=t;
}
第2关:编程实现最大子段和问题的求解(分别采用分治法和动态规划法求解)
题目描述:
对于给定序列a1,a2,a3……an,寻找它的某个连续子段,使得其和最大。如( -2,11,-4,13,-5,-2 )最大子段是{ 11,-4,13 }其和为20。
输入格式
第一行输入一个n,代表有n个数
下面一行输入n个数,代表序列
0 序列中的数的范围为[-2000,2000]
输出格式
输出一个数,代表最大字段和。
测试输入:
6
-2 11 -4 13 -5 -2
预期输出:
20
代码:
第一种:分治法
#include
int Largestsubsection(int a[],int m,int n){
if(n==m){
return a[n];
}
int cent=(m+n)/2;
int d=Largestsubsection(a,m,cent)>Largestsubsection(a,cent+1,n)?Largestsubsection(a,m,cent):Largestsubsection(a,cent+1,n);
int left=0,s1=0;
for(int i=cent;i>=m;i--){
left+=a[i];
if(left>s1){
s1=left;
}
}
int right=0,s2=0;
for(int i=cent+1;i<=n;i++){
right+=a[i];
if(right>s2){
s2=right;
}
}
int sum=s1+s2;
return d>sum?d:sum;
}
int main(){
int n;
scanf("%d",&n);
int a[n];
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
}
printf("%d",Largestsubsection(a,0,n-1));
return 0;
}
第二种:动态规划法
#include
int main(){
int n;
scanf("%d",&n);
int a[n][2];
int max=0;
for(int i=0;i<n;i++){
scanf("%d",&a[i][0]);
if(i==0){
a[i][1]=a[i][0];
}
else{
a[i][1]=a[i-1][1]+a[i][0]>a[i][0]?a[i-1][1]+a[i][0]:a[i][0];
}
max=max>a[i][1]?max:a[i][1];
}
printf("%d",max);
return 0;
}
第3关:0-1背包
题目描述:
给定n个物品和一背包,物品i的重量是wi,其价值为vi,背包的容量为c。问应如何选择装入背包中的物品,使得装入背包中物品的总价值最大?
相关知识
动态规划
输入格式
第一行输入一个n,c,代表有n个物品背包容量为c
接下来n行,每行输入wi,和vi
其中0
输出格式
输出一个数,代表最大价值
测试输入:
4 8
2 3
3 4
4 5
5 6
预期输出:
10
代码:
#include
int main(){
int n,c;
scanf("%d %d",&n,&c);
int a[n][2],b[n][c+1]={
0};
for(int i=0;i<n;i++){
scanf("%d %d",&a[i][0],&a[i][1]);
for(int j=1;j<c+1;j++){
if(i==0){
if(a[i][0]>j){
b[i][j]=0;
}
else{
b[i][j]=a[i][1];
}
}
else{
if(a[i][0]<=j){
b[i][j]=b[i-1][j]>a[i][1]+b[i-1][j-a[i][0]]?b[i-1][j]:a[i][1]+b[i-1][j-a[i][0]];
}
else{
b[i][j]=b[i-1][j];
}
}
}
}
printf("%d",b[n-1][c]);
return 0;
}
第4关:最长单调子序列
题目描述
给定一个序列,求这个序列的最长上升子序列的长度,并输出这个最长上升子序列,题目保证,最长上升子序列只有一个。
相关知识
最长上升子序列
输入格式
第一行输入一个n,代表序列长度
第二行输入n个值,代表这个序列
0 -1000<序列内的数<1000
输出格式
第一行输出一个数,代表最长上升子序列的长度。
第二行打印这个子序列。
测试输入:
8
5 2 8 6 3 6 5 7
输出
4
2 3 6 7
代码:
#include
int main(){
int n;
scanf("%d",&n);
int m[n][3];
m[0][1]=1;
m[0][2]=0;
for(int i=0;i<n;i++){
scanf("%d",&m[i][0]);
if(i!=0){
int k=i-1;
while(k>=0){
if(m[i][0]>m[k][0]){
m[i][1]=m[k][1]+1;
m[i][2]=k;
break;
}
k--;
}
if(k<0){
m[i][1]=1;
m[i][2]=i;
}
}
}
int max=m[0][1],j=0;
for(int i=0;i<n;i++){
if(m[i][1]>=max){
max=m[i][1];
j=i;
}
}
printf("%d\n",max);
int a[max],c=max;
while(m[j][2]!=j){
max--;
a[max]=m[j][0];
j=m[j][2];
}
a[max-1]=m[j][0];
for(int i=0;i<c;i++){
printf("%d ",a[i]);
}
return 0;
}
第5关:最长公共子序列(LCS)
题目描述
给定两个序列X=ABCBDAB, Y=BDCABA,求X和Y的最长公共子序列
相关知识
动态规划
输入格式
两行,每行为一个长度不超过500的字符串(全为大写字符串)
输出格式
输出一个数,最长公共子序列的长度,若不存在公共子序列,则输出0。
测试输入
ABCBDAB
BDCABA
测试输出
4
代码:
方法一:
(自己想的)
#include
#include
int main(){
char x[500]={
'\0'},y[500]={
'\0'};
scanf("%s",x);
scanf("%s",y);
int m=strlen(x),n=strlen(y);
int a[m][n];
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(x[i]==y[j]){
a[i][j]=1;
}
else{
a[i][j]=0;
}
}
}
int max=0;
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(a[i][j]==1){
int t=1,k=i+1,l=j+1;
while(k<m&l<n){
int g=l;
while(l<n&&a[k][l]==0){
l++;
}
if(a[k][l]==1){
t++;l++;}
else{
l=g;
}
k++;
}
max=max>t?max:t;
}
}
}
printf("%d",max);
return 0;
}
方法二:
(算法课上讲的DP方法)
#include
#include
int main(){
char x[500]={
'\0'},y[500]={
'\0'};
scanf("%s",x);
scanf("%s",y);
int m=strlen(x),n=strlen(y);
int a[m+1][n+1]={
0};
for(int i=1;i<m+1;i++){
for(int j=1;j<n+1;j++){
if(x[i]==y[j]){
a[i][j]=a[i-1][j-1]+1;
}
else{
a[i][j]=a[i][j-1]>a[i-1][j]?a[i][j-1]:a[i-1][j];
}
}
}
printf("%d",a[m][n]);
return 0;
}
注:答案不唯一,仅供参考。