动态规划

前言

最头疼的就是这一块 老师上课又飞快,虽然讲的非常好,但是还是有点跟不上节奏,加之老师根本不分析代码,所以自己考前准备自己动手复习一波。

正文

动态规划解决问题的基本方法码一波
(1)找出最优解的性质并刻画其结构特征
(2)递归地定义最优值
(3)以自底向上的方式计算出最优值
(4)根据计算最优值时的信息构造最优解

1.矩阵连乘问题

矩阵连乘的最优值是n个矩阵连乘的最小数乘次数,最优解是得出矩阵之间该如何进行优先级运算才能得出最优值(也就是加括号的方式)

题目描述
给定n个矩阵{A1,A2,…,An},其中Ai与Ai+1是可乘的,i=1,2 ,…,n-1。如何确定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少。
A1={30x35} ; A2={35x15} ;A3={15x5} ;A4={5x10} ;A5={10x20} ;A6={20x25} ;
最后的结果为:((A1(A2A3))((A4A5)A6)) 最小的乘次为15125
m[i][j]的递推方程

Code

#include<iostream>
using namespace std;
int m[100][100];     //m[i][j]表示A[i:j]的最小数乘次数 
int s[100][100]={0};     //s[i][j]表示断点位置 
int p[100];		//表示矩阵的行列值 
int matrixchain(int n)
{
	for(int i=0;i<=n;i++)
	{
		m[i][i]=0;  //对角线赋值0 
	}
	for(int r=2;r<=n;r++)
	{
		for(int i=1;i<=n-r+1;i++)
		{
			int j=i+r-1;     //从m[i][i+1]开始判断 i<=j<n
			m[i][j]=m[i+1][j]+p[i-1]*p[i]*p[j];
			s[i][j]=i;       //此时从i处断开
			for(int k=i+1;k<j;k++)
			{
				int t=m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j]; //从i+1处断开并比较大小 
				if(t<m[i][j])
				{
					m[i][j]=t;         //更新最优值 
					s[i][j]=k;		   //更新断点 
				}
			}
		}
	}
 } 
 int main()
 {
 	int N;
	cin>>N;
	for(int i=0;i<N+1;i++)
	{
		cin>>p[i];
	 } 
	matrixchain(N);
	cout<<m[1][N]<<endl;
	system("pause");
	return 0;
 }

2.最长公共子序列

题目描述
给你一个序列X和另一个序列Z,当Z中的所有元素都在X中存在,并且在X中的下标顺序是严格递增的,那么就把Z叫做X的子序列。
例如:Z=<a,b,f,c>是序列X=<a,b,c,f,b,c>的一个子序列,Z中的元素在X中的下标序列为<1,2,4,6>。
现给你两个序列X和Y,请问它们的最长公共子序列的长度是多少?

code

#include<iostream>
using namespace std;
int c[100][100];
int b[100][100];
char x[100],y[100];
int lcslength(int m,int n)     //构造最优值,以及最优解需要的条件b[i][j]
{
	int i,j;
	for(i=1;i<=m;i++)c[i][0]=0;
	for(i=1;i<=n;i++)c[0][i]=0;
	for(i=1;i<=m;i++)
	{
		for(j=1;j<=n;j++)
		{
			if(x[i]==y[j])
			{
				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][j]=c[i-1][j];
					b[i][j]=2;
				}
				else
				{
					c[i][j]=c[i][j-1];
					b[i][j]=3;
				}
			}
		}
	}
}                              
void lcs(int i,int j)      //构造最优解
{
	if(i==0||j==0)
	{
		return;
	}
	if(b[i][j]==1)
	{
		lcs(i-1,j-1);
		cout<<x[i]<<" ";
	}
	else if(b[i][j]==2)
	{
		lcs(i-1,j);
	}
	else
	{
		lcs(i,j-1);
	}
}
int main()
{
	int m,n;
	cin>>m>>n;
	for(int i=1;i<=m;i++)
	{
		cin>>x[i];
	}
	for(int i=1;i<=n;i++)
	{
		cin>>y[i];
	}
	lcslength(m,n);
	cout<<"length="<<c[m][n]<<endl;
	lcs(m,n);
	return 0;
}

3.最大子段和

题目描述
给定n个整数组成的序列a1,a2,…an, 求子段和ai+ai+1+…+aj(子段可为空集)的最大值。
提示:子段可为空集,答案为0

输入

包含多组测试数据。第一行为一个整数T(1<=T<=20),代表测试数据个数。
每组测试数据第一行为一个整数n,代表有n个整数(1<=n<=10000)。
接下来一行有n个数x(-1000<=x<=1000)。
1
6
2 -11 4 13 -1 2

输出

输出其对应的最大子段和。
18

code

#include<iostream>
using namespace std;
int a[100];
int sum=0;
int maxsum(int n)
{
	int b=0;
	for(int i=0;i<n;i++)
	{
		if(b>0)
		{
			b+=a[i];
		}
		else 
		{
			b=a[i];
		}
		if(b>sum)
		{
			sum=b;
		}
	}
}
int main()
{
	int k;
	cin>>k;
	for(int i=0;i<k;i++)
	{
		cin>>a[i];
	}
	maxsum(k);
	cout<<sum<<endl;
	return 0;	
}

4.0-1背包动态规划解法

题目描述
已知有N种物品和一个可容纳C重量的背包。每种物品i的重量为Wi,价值为Pi。那么,采用怎样的装包方法才会使装入背包物品的总价值最大。

输入

包含多组测试数据。第一行为一个整数T(1<=T<=10),代表测试数据个数。
接下来有T组测试数据。每组测试数据第一行为背包的重量C(C<10000)和物品个数N(N<1000)。接下来的N行分别为物品的重量cost和价值
(注意:结果可能超过int范围)
1
10 5
2 6
2 3
6 5
5 4
4 6

输出

对每组测试数据,输出其对应的所装物品的最大价值。
15

code

#include<iostream>
using namespace std;
int m[100][100]={0};
int w[100];
int v[100];
int knapsack(int n,int c)
{
	int jmax=min(w[n]-1,c);
	for(int i=0;i<=jmax;i++)
	{
		m[n][i]=0;
	}
	for(int i=w[n];i<=c;i++)
	{
		m[n][i]=v[n];
	}
	for(int i=n-1;i>=1;i--)
	{
		jmax=min(w[i]-1,c);
		for(int j=0;j<=jmax;j++)
		{
			m[i][j]=m[i+1][j];
		}
		for(int j=w[i];j<=c;j++)
		{
			m[i][j]=max(m[i+1][j],m[i+1][j-w[i]]+v[i]);
		}
	}
}
int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		int c,n;
		cin>>c>>n;
		for(int i=1;i<=n;i++)
		{
			cin>>w[i]>>v[i];
		}
		knapsack(n,c);
		cout<<m[1][c]<<endl;
	}
	return 0;
}

例题

5.节食的限制

题目描述
Bessie像她的诸多姊妹一样,因為从FarmerJohn的草地吃了太多美味的草而长出了太多的赘肉。所以FJ将她置於一个及其严格的节食计划之中。她每天不能吃多过H(5<=H<=45000)公斤的乾草。Bessie只能吃一整綑乾草;当她开始吃一綑乾草的之后就再也停不下来了。她有一个完整的N(1<=n<=50)綑可以给她当作晚餐的乾草的清单。她自然想要尽量吃到更多的乾草。很自然地,每綑乾草只能被吃一次(即使在列表中相同的重量可能出现2次,但是这表示的是两綑乾草,其中每綑乾草最多只能被吃掉一次)。给定一个列表表示每綑乾草的重量Si(1<=Si<=H),求Bessie不超过节食的限制的前提下可以吃掉多少乾草(注意一旦她开始吃一綑乾草就会把那一綑乾草全部吃完)。
输入

第一行:两个由空格隔开的整数:H和N, 第2到N+1行:第i+1行是一个单独的整数,表示第i綑乾草的重量Si。
56 4
15
19
20
21  

输出

一个单独的整数表示Bessie在限制范围内最多可以吃多少公斤的乾草。
56

code

#include<iostream>
#include<algorithm>
#include<string.h>
#define inf 0x7fffffff
using namespace std;

int main()
{
	int c; //最多吃的干草量
	int n; //有多少捆干草
	cin >> c >> n;
	int *wei = new int[n + 1];
	int m[50000];
	for (int i = 1; i <= n; i++)
	{
		cin >> wei[i];
	}
	memset(m, 0, sizeof(m));
	for (int i = 1; i <= n; i++)
	{
		for (int j = c; j >= wei[i]; j--) //j从大到小倒着遍历,如果大于重量,则说明可以放入,反之,则放弃这个物品,从下一个物品开始
		{
			m[j] = max(m[j], m[j - wei[i]] + wei[i]);
		}
	}
	cout << m[c] << endl;
}

矩阵连乘进阶

题目描述

给定n个矩阵{A1,A2,...,An},及m个矩阵连乘的表达式,判断每个矩阵连乘表达式是否满足矩阵乘法法则,如果满足,则计算矩阵的最小连乘次数,如果不满足输出“MengMengDa“。
输入

输入数据由多组数据组成(不超过10组样例)。每组数据格式如下:
第一行是2个整数n (1≤n≤26)和m(1≤m≤3),表示矩阵的个数。
接下来n行,每行有一个大写字母,表示矩阵的名字,后面有两个整数r和c,分别表示该矩阵的行数和列数,其中1<r, c<100。
第n+1行到第n+m行,每行是一个矩阵连乘的表达式(2<=矩阵个数<=100)。
输出

对于每个矩阵连乘表达式,如果运算不满足矩阵乘法法则的情况(即左矩阵列数与右矩阵的行数不同),则输出“MengMengDa”,否则输出最小矩阵连乘次数。

数据保证结果不超过1e9。

样例输入

3 2
A 10 100
B 5 50
C 100 5
ACB
ABC

样例输出

7500
MengMengDa

code

#include<iostream>
#include<string.h>
using namespace std;
int m[100][100]={0};
int p[200];
int maxchain(int n)
{
	for(int i=1;i<=n;i++)
	{
		m[i][i]=0;
	}
	for(int r=2;r<=n;r++)
	{
		for(int i=1;i<=n-r+1;i++)
		{
			int j=i+r-1;
			m[i][j]=m[i+1][j]+p[i-1]*p[i]*p[j];
			for(int k=i+1;k<j;k++)
			{
				int t=m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j];
				if(t<m[i][j])
				{
					m[i][j]=t;
				}
			}
		}
	}
	return m[1][n];
}
int main()
{
	int n,m;
	while(cin>>n>>m)
	{
	
		char chain[30];
		int h[30];
		int l[30];
		for(int i=0;i<n;i++)
		{
			cin>>chain[i]>>h[i]>>l[i];
		}
		char test[3][105];
		for(int i=0;i<m;i++)
		{
			cin>>test[i];
		}
		int key;
		for(int i=0;i<m;i++)
		{
			for(int j=0;j<n;j++)
			{
				if(test[i][0]==chain[j])
				{
					key=j;
					break;
				}
			}
			p[0]=h[key];
			p[1]=l[key];
			int len=strlen(test[i]);
			int flag=1;
			for(int k=1;k<len;k++)
			{
				for(int j=0;j<n;j++)
				{
					if(test[i][k]==chain[j])
					{
						key=j;
						break;
					}
				}
				if(p[k]==h[key])
				{
					p[k+1]=l[key];
				}
				else
				{
					cout<<"MengMengDa"<<endl;
					flag=0;
					break;
				}
			}
			if(flag==1)
			{
				cout<<maxchain(len)<<endl;
			}
		}
	}
}
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值