动态规划法

动态规划法

矩阵连乘问题

#include<bits/stdc++.h>
using namespace std;

int m[20][20];    //记录i--j的最小次数
int s[20][20];    //记录i--j最小次数的划分位置      将他们均作为全局变量,在接下来函数调用时可以不再定义
int p[20];        //存放输入的行列值
int n;            //记录矩阵数

void matrixchain(){
//    for(int i=1;i<=n;i++){      //可以不用将数组对角线初始化为0,因为下面没有用到
//        m[i][i]=0;
//    }
    //memset(m,0,sizeof(m));
    for(int r=1;r<=n-1;r++){                  //r理解为用来控制输出的行数,由于m[6][6]的结果为0,所以循环进行了n-1次
        for(int i=1;i<=n-r;i++){                  //控制每斜行每个元素的开头i理解为用来控制每行元素的个数,特别注意计算的时候不是按一行一行来的
            int j=i+r;
            m[i][j]=m[i+1][j]+p[i]*p[i+1]*p[j+1];    //将在i位置处划分的结果计算出来,暂时作为最小值
            s[i][j]=i;                             //记下i的位置,即从i--j的最小划分位置为i
            for(int k=i+1;k<j;k++){              //从下一次继续循环划分,至小于j
                int t=m[i][k]+m[k+1][j]+p[i]*p[k+1]*p[j+1];         //将不同划分处计算数记下
                if(t<m[i][j]){                         //比较与原划分的大小,继而决定是否将最小数和划分位置进行改变
                    m[i][j]=t;
                    s[i][j]=k;
                }
            }
        }
    }
}

void traceback(int lx,int rx){
    if(lx==rx)
        cout<<"A"<<lx;          //当划分结束,可以输出矩阵
    else{
        cout<<"(";
        traceback(lx,s[lx][rx]);     //s[lx][rx]存放着lx--rx的最优的划分位置
        traceback(s[lx][rx]+1,rx);   //将lx--rx,按最优划分位置分成两部分,再进行递归,当lx=rx时,无法继续进行划分,则输出
        cout<<")";
    }
}

int main(){
    cout<<"请输入矩阵的个数:";
    cin>>n;
    cout<<"请依次输入各矩阵的行和最后矩阵的列:"<<endl;
    for(int i=1;i<=n+1;i++){                           //将输入的矩阵行列值放入p数组,注意输入时从下标1开始,后面会更好理解
        cin>>p[i];
    }
    matrixchain();                                    //调用matrixchain()函数,计算最小次数二维数组m[i][j]
    cout<<"最小乘法次数:"<<m[1][n]<<endl;        //输出最小次数
    cout<<"划分次序为:";
    traceback(1,n);                     //输出加括号的方式
    return 0;
}

在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;

int c[20][20];           //c[i][j]:X序列前i个元素与Y序列前j个元素最长公共子序列个数
int b[20][20];           //b[i][j]:X序列前i个元素与Y序列前j个元素最长公共子序列由哪种情况得来
int num;           //X序列的个数
int numm;          //y序列的个数

int lcslength(char x[], char y[]){         //lcslength算法输出最长公共子序列的元素数
    for(int i=0;i<=num;i++) c[i][0]=0;
    for(int i=0;i<=numm;i++) c[0][i]=0;     //因为当任一序列元素数为0时,公共子序列元素数定为0,故此C[0][i] c[i][0]初始化为0
        
    for(int i=1;i<=num;i++){               //行数为序列1的元素数
        for(int j=1;j<=numm;j++){          //列数为序列2的元素数,通过循环按行计算子序列数
                                    
            if(x[i]==y[j]){              //如果序列两元素相等,那么现在子序列个数为去掉这两个元素后序列子序列的元素数+1
                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-1][j]与c[i][j-1],谁大谁给c[i][j]
                c[i][j]=c[i-1][j];
                b[i][j]=2;
            }
            else{
                c[i][j]=c[i][j-1];
                b[i][j]=3;                   //b[i][j]用于标记哪一种情况
            }
        }
    }
    cout<<c[num][numm];            //最后c[num][numm]即为两序列最长公共子序列的元素数
}


void lcs(int i,int j,char x[]){    //输出最长公共子序列
    if(i==0||j==0)  return;
    if(b[i][j]==1){             //如果是情况1:x[i]=y[j],那么递归lcs(i-1,j-1,x)
		lcs(i-1,j-1,x);
		cout<<x[i];            //  输出相同的元素,因为是从后向前,所以递归完成后再输出   
	}
	else if(b[i][j]==2){       //如果是情况2:x[i]!=y[j],那么递归lcs(i-1,j,x)
		lcs(i-1,j,x);
	}
	else lcs(i,j-1,x);         //如果是情况3:x[i]!=y[j],那么递归lcs(i,j-1,x)
	                           //注意:此时可以连续用三个if,不能前两个是if最后一个是else
}

int main(){

    cin>>num;
    char x[num+1];
    for(int i=1;i<=num;i++){    //输入系列1放入数组X
        cin>>x[i];
    }
    cin>>numm;
    char y[numm+1];
    for(int i=1;i<=numm;i++){   //输入序列2放入数组Y 
        cin>>y[i];
    }

    lcslength(x,y);          //利用算法,求出序列的最长公共子序列的元素数
    cout<<endl;
    lcs(num,numm,x);

   return 0;
}

通过最长公共子序列问题求解,得到一个特别注意事项
如果有三种假设是不会同时成立的,一定要用:
if else if else
只有这样才能避免程序中的意外情况发生。

在这里插入图片描述

蓝桥杯39级台阶问题

共39级台阶,如果我每一步只能迈上1个或2个台阶。先迈左脚,然后左右交替,最后一步是迈右脚,也就是说一共要走偶数步。那么,上完39级台阶,有多少种不同的上法呢?

动态规划算法:
#include<bits/stdc++.h>
using namespace std;
//动态规划(递推)
int main() {          
	int dp[40];             //定义一个数组dp[], 其中dp[i]是走到i台阶时候的方法数
	dp[0] = 0;
	dp[1] = 0;
	dp[2] = 1;             //对数组进行初始化
	dp[3] = 2;
	dp[4] = 2;
	for(int i = 5; i <= 39; i++) {
		dp[i] = dp[i - 2] + dp[i - 3] * 2 + dp[i - 4];
//题目限定了条件,一定要偶数步,每一步可以走1 或者 2步。
//踏上dp[i]的是右脚,要想踏上dp[i], 可以从第i - 2踏上两个一级,所以加上dp[i - 2], 
//还可以从第i - 3踏上1级,2级或者2级,1级,所以加上dp[i - 3] * 2, 
//最后还可以从第i - 4,踏上两个2级(可能会有疑问,不可以1,1,1,1这样?
//如果这样,就回到了第i - 2当中去了,dp[i - 2]包括在内了。)所以又加上dp[i - 4];

	}
	cout << dp[39] << endl;

}

递归算法:
#include<bits/stdc++.h>
using namespace std;

int m=0;
void q(int step,int level){
    if(level<0) return;
    if(step%2==0&&level==0){
        m++;
        return;              //加上return,当程序找到答案时给予返回,重新寻找合适的走法;
                             //不加return也对,但是程序会继续向下执行,知道找不到答案而返回,故会浪费时间
        }
    q(step+1,level-1);      //一直执行,不停+1 -1,结果不行则返回,执行下面那句+1 -2,不行则返回上层,再+1 -2
	q(step+1,level-2);

}
int main(){
    q(0,39);
    cout<<m;
    return 0;

}

0-1背包问题

#include<bits/stdc++.h>
using namespace std;

int w[20],v[20];
int dp[1000][1000]={0};
int n,m;
int x[20]={0};

void traceback(){
    for(int i=n;i>=2;i--){
        if(dp[i][m]!=dp[i-1][m]){
            x[i]=1;
            m=m-w[i];
        }
    }
    x[1]=(dp[1][m]>0)? 1:0;
}


int main()
{
                //n:物品的个数   m:背包最大承载总质量
	cin>>n>>m;
	int i,j;
	for(i=1;i<=n;i++)
      cin>>w[i]>>v[i];       //以此录入物品质量放进w[]数组,价值放入v[]
	for(i=1;i<=n;i++)
	{
		for(j=0;j<=m;j++)
		{
			if(j<w[i])                //若第i个物品,质量w[i]超过总载重j,则无法放入,此时价值和放入前i-1个时相同
			  dp[i][j]=dp[i-1][j];
			else                      //如果没有超过总载重
			  dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);     //找最大的
		}
	}

	cout<<dp[n][m]<<endl;
	traceback();
	for(int i=1;i<=n;i++){
        cout<<x[i];
	}
	return 0;
}



最大子序列和

#include<bits/stdc++.h>
using namespace std;

int main(){
    int num,flag,flagg;
    int a[100];
    cin>>a[0];
    while(cin.get()!='\n'){
        cin>>a[num++];
    }
    int dp[num]={0};
    int maxx=dp[0];

    for(int i=1;i<=num-1;i++){
        dp[i]=max(a[i],dp[i-1]+a[i]);
        if(dp[i]>maxx){
            maxx=dp[i];
            flag=i;
            flagg=flag;
        }
    }
    cout<<maxx<<endl;
    while(maxx!=0){
        maxx=maxx-a[flag];
        flag--;
    }
    for(int i=flag+1;i<=flagg;i++){
        cout<<a[i]<<" ";
    }
    return 0;
}

最长单调递增子序列

设计一个O(n^2)时间的算法,找出由n个数组成的序列的最长单调递增子序列
要求,输出子序列及其长度

#include<bits/stdc++.h>
using namespace std;

int b[100];
int c[100];
int temp=0;
int m=0;
int LISdyna(int a[], int n){
    b[1]=1;
    int k=0;
    for(int i=2;i<=n;i++){
        for(int j=1;j<i;j++){
            if(a[i]>=a[j]&&k<b[j]){
                k=b[j];
            }
        }
        b[i]=k+1;
    }

    //sort(b+1,b+n+1);         //若只要求输出子序列长度,则可以直接调用sort()函数排序即可
   //cout<<b[n];
   for(int i=1;i<=n;i++){
      if(b[i]>temp){
        temp=b[i];
        m=i;
      }
   }
   cout<<"最大单调递增子序列长为:";
   cout<<temp<<endl;

}

int main(){
    int a[100];
    int n=1;
    cin>>a[n++];
    while(cin.get()!='\n'){
        cin>>a[n++];
    }
    LISdyna(a,n-1);
    c[0]=a[m];
    int t=1;
    for (int i = m-1; i >=0&&temp-t>0; i--)
    {

        if(b[i]==temp-t){
       	   c[t++]=a[i];
        }
    }
    cout<<"序列为:";
    for(int i=t-1;i>=0;i--){
    	cout<<c[i]<<" ";
    }
    return 0;


}

发布了16 篇原创文章 · 获赞 0 · 访问量 535

猜你喜欢

转载自blog.csdn.net/clfxyz/article/details/103112497