动态规划法
矩阵连乘问题
#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;
}