动态规划

动态规划常见题目解析:

分别为:

1.最长公共子序列

2.最大字段和

3.0-1背包问题

下面逐一描述:

1.最长公共子序列

题目描述:

例如对于X={A,B,C,B,D,A,B},Y={B,D,C,A,B,A},则序列{B,C,B,A}就是X和Y的最长公共子序列。

解法:

按照该思路的解法:

#include <iostream>
using namespace std;
//动态规划算法-最长公共子序列

//m,n代表数组x和y的长度,c[i][j]表示Xi和Yj的最长公共子序列的长度,
//b[i][j]记录c[i][j]是由哪一种子问题的解得到的
void LCSLength(int m,int n,char x[],char y[],int c[][7],int b[][7])
{
	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,char x[],int b[][7])
{
	if (i==0 || j==0) return;
	if (b[i][j]==1)
	{
		LCS(i-1,j-1,x,b);
		cout<<x[i]<<'\t';
	}else if (b[i][j]==2)
		LCS(i-1,j,x,b);
	else
		LCS(i,j-1,x,b);
}


int main()
{
	char x[]={'Z','A','B','C','B','D','A','B'};//长度为8
	char y[]={'Z','B','D','C','A','B','A'};//长度为7
	int c[8][7];
	int b[8][7];
	LCSLength(7,6,x,y,c,b);
	LCS(7,6,x,b);
	cout<<endl;
	return 0;
}

 

2.最大字段和

问题描述:给定n个整数(可能有负整数)组成的序列a1,a2,...,an,求ai...aj的最大值,0<=i,j<n,例如当数组为{-2,11,-4,13,-5,-2}时,最大字段和为11+(-4)+13=20.

解法描述:

 

#include <iostream>
using namespace std;
//解法一:时间复杂度为O(n^2)
//n表示数组的长度,besti和bestj分别表示大子段的起止位置
int MaxSum1(int n,int a[],int &besti,int &bestj)
{
	int sum=0;
	for (int i=0;i<n;i++)
	{
		int tempsum=0;
		for (int j=i;j<n;j++)
		{
			tempsum+=a[j];
			if (tempsum>sum)
			{
				sum=tempsum;
				besti=i;
				bestj=j;
			}
		}
	}
	return sum;
}
//解法二:时间复杂度为O(n)
int MaxSum2(int n,int a[],int &besti,int &bestj)
{
	int sum=0,b=0;
	for (int i=0;i<n;i++)
	{
		if (b>0)
		{
			b+=a[i];
			if (a[i]>=0)//最后一个数字肯定是非负数
				bestj=i;
		}
		else
		{
			b=a[i];
			besti=i;//第一个数字肯定也是非负数,但无需判断
		}
		if (b>sum)
			sum=b;
	}
	return sum;
}

int main()
{
	int arr[6]={-2,11,-4,13,-5,-2};
	int besti,bestj;
	//cout<<MaxSum1(6,arr,besti,bestj)<<endl;
	cout<<MaxSum2(6,arr,besti,bestj)<<endl;
	cout<<besti<<"---"<<bestj<<endl<<"最大字段和序列如下:"<<endl;
	for (int i=besti;i<=bestj;i++)
	{
		cout<<arr[i]<<endl;
	}
	return 0;
}

3.0-1背包问题

问题描述:给定n种物品和一背包,物品i的重量是w[i],其价值为v[i]背包的容量为c,问应如何选择放入背包的物品是背包的总价值最大?

说明:物品只有两种选择:放入背包和不放入背包。

解法1描述:(效率较低,有条件限制:背包重量必须为正整数)

 

#include <iostream>
#define min(x,y)((x)>(y)?y:x)
#define max(x,y)((x)<(y)?y:x)
using namespace std;

//动态规划-0-1背包问题

//n表示有n个物品,v[n]存储的是物品价值,w[n]存储的是物品重量
//m[i][j]表示背包容量为j,可选物品是i,i+1,...,n时背包问题的最优解
//m[i][j]的递归式如下:
//m[i][j] = 1.max{m[i+1,j],m[i+1,j-w[i]]+v[i]}	j>=w[i];
//			2.m[i+1,j]							0<=j<w[i]
//m[n][j] = 1.v[n]								j>=w[n];	
//			2.0									0<=j<w[n]
//递归式解释:i=n的情况不需要解释,很容易理解;对于i的情况,如果0<=j<w[i],则i元素肯定不放在
//背包中,所以和m[i+1,j]是相等的,如果j>=w[i],也就是说i元素有放入背包的可能,如果不放入背包,和
//0<=j<w[i]的情况是一样的,即m[i+1,j],如果放入背包,就是先要求出背包元素为i+1至n,背包重量为j-w[i]的
//最优解即m[i+1,j-w[i]]然后再加上v[i],最后比较这这两个值的最大值。
//说明:该算法结束之后,容易看出m[1][c]即为背包问题的最优解
//v,w中的元素是从0->n-1;m中的元素是从1->n。
void Knapsack(int v[],int w[],int c,int n,int m[][11])
{
	int i,j;
	//首先计算当i=n-1的时候背包的最优解
	int jMax=min(w[n-1],c);
	//下面的两个语句考虑到了最小值为w[n-1]或者是c的情况
	for (j=0;j<jMax;j++)//当j<jMax时,求相应的最优解
		m[n][j]=0;
	for (j=w[n-1];j<=c;j++)//当w[n-1]<=j<=c的时候,求相应的最优解,如果c<w[n-1],不执行下面的语句,上面的语句已经求出最优解
		m[n][j]=v[n-1];

	//然后计算0<i<n-1时的最优解
	for (i=n-2;i>0;i--)
	{
		jMax=min(w[i],c);
		for (j=0;j<jMax;j++)
			m[i+1][j]=m[i+2][j];
		for (j=w[i];j<=c;j++)
			m[i+1][j]=max(m[i+2][j],m[i+2][j-w[i]]+v[i]);
	}

	//最后计算i=0时的最优解
	m[1][c]=m[2][c];
	if (c>=w[0])
		m[1][c]=max(m[2][c],m[2][c-w[0]]+v[0]);
}

//将最优解构造出来,放到数组x[]中
void Traceback(int m[][11],int w[],int c,int n,int x[])
{
	for (int i=1;i<n;i++)
	{
		if (m[i][c]==m[i+1][c])//说明i不在背包中
			x[i]=0;
		else//i在背包中
		{
			x[i]=1;
			c-=w[i-1];
		}
	}		
	x[n]=(m[n][c])>0?1:0;
}


int main()
{
	int w[]={2,2,6,5,4};
	int v[]={6,3,5,4,6};
	int c=10;
	int x[6];
	int m[6][11];
	Knapsack(v,w,c,5,m);
	Traceback(m,w,c,5,x);
	cout<<"最优解是:"<<endl;
	for (int i=1;i<=5;i++)
		cout<<i<<":"<<x[i]<<endl;
	return 0;
}

 解法1描述:(效率提升)

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值