动态规划.

概述

        动态规划是最优子结构or子问题最优性的一般解法的设计方法的策略,本质上是递推,核心是找到状态转移的方式,写出dp方程。

例题

1.动态规划-背包问题(01背包)-排序法

        有n个物体,质量和价值分别为wi,vi的物品,从这些物品中挑出总重量不超过w的物品,求所有挑选方案中价值总和的最大值。

题目分析:和部分背包有别的是他不能选择一个物体的一小部分,只能选和不选,故称为01背包。仍然是在有限的最大承载质量中选出价值最高的。我们仍然可以对价值/质量的值进行排序。这样从比值高的开始选。

//动态规划
//01背包-排序法 
/*
有n个物体,质量和价值分别为wi,vi的物品,
从这些物品中挑出总重量不超过w的物品,
求所有挑选方案中价值总和的最大值 
*/ 
/*
输入
n=4
(w,v)={(2,3),(1,2),(3,4),(2,2)}
w=5

输出:
7(选择第0,1,3号物品) 

因为对每一个物体只有选择和不选择两种情况,所以这个问题称为01背包 
*/
#include<stdio.h>
//三个数组分别是存放质量,存放价值,存放价值与质量的比值 
void get_sort(int weigth[],int value[],float ratio_w_v[],int n);
//变量n为物体个数
//变量w为最大承载质量 
void get_value(int weigth[],int value[],int n,int w);
int main(void)
{
	int w,n;
	scanf("%d %d",&w,&n);
	int weight[n];
	int value[n];
	float ratio_w_v[n];
	for(int i=0;i<n;i++)
	{
		scanf("%d %d",&weight[i],&value[i]);
		ratio_w_v[i]=(float)value[i]/(float)weight[i];
	}
	get_sort(weight,value,ratio_w_v,n);
	get_value(weight,value,n,w);
	return 0;
 } 
 //这里按照价值与质量的比值升序排列 
 void get_sort(int weight[],int value[],float ratio_w_v[],int n)
 {
 	for(int i=0;i<n-1;i++)
 	{
 		for(int j=i+1;j<n;j++)
 		{
 			if(ratio_w_v[i]<ratio_w_v[j])
 			{
 				int temp0;
 				temp0=weight[i];
 				weight[i]=weight[j];
 				weight[j]=temp0;
 				
 				temp0=value[i];
 				value[i]=value[j];
 				value[j]=temp0;
 				
 				float temp1;
 				temp1=ratio_w_v[i];
 				ratio_w_v[i]=ratio_w_v[j];
 				ratio_w_v[j]=temp1;
 				
			 }
		 }
	 }
 }
 void get_value(int weight[],int value[],int n,int w)
 {
 	int weight_01bag=0;//存放存放当前质量 
 	int value_01bag=0;//存放当前价值 
 	for(int i=0;i<n;i++)
	{
		weight_01bag+=weight[i];
		value_01bag+=value[i];
		if(weight_01bag>w)//遇到小于等于的情况就直接加,但凡遇到大于的就给他减回去 
		{
			weight_01bag-=weight[i];
			value_01bag-=value[i];
		}
	} 
	printf("%d\n",value_01bag);
	return ;
 }

2.动态规划-背包问题(01背包)-深搜法

        有n个物体,质量和价值分别为wi,vi的物品,从这些物品中挑出总重量不超过w的物品,求所有挑选方案中价值总和的最大值。

题目分析:既然是选和不选,那么我们就可以用深搜去选和不选来求出答案

//动态规划
//01背包-深搜法(选和不选)
/*
有n个物体,质量和价值分别为wi,vi的物品,
从这些物品中挑出总重量不超过w的物品,
求所有挑选方案中价值总和的最大值 
*/ 
/*
输入
n=4
(w,v)={(2,3),(1,2),(3,4),(2,2)}
w=5

输出:
7(选择第0,1,3号物品) 

因为对每一个物体只有选择和不选择两种情况,所以这个问题称为01背包 
*/
#include<stdio.h>
void get_value(int weigth[],int value[],int n,int w,int weight_01bag,int value_01bag,int nn);
int max_value=0;
int main(void)
{
	//w表示最大承载的质量,n表示有多少个物体  
	int w,n;
	scanf("%d %d",&w,&n);
	int weight[n];
	int value[n];
	int weight_01bag=0;
	int value_01bag=0;
	for(int i=0;i<n;i++)
	{
		scanf("%d %d",&weight[i],&value[i]);
	}
	get_value(weight,value,0,w,weight_01bag,value_01bag,n);
	printf("%d",max_value);
	return 0;
} 
void get_value(int weight[],int value[],int n,int w,int weight_01bag,int value_01bag,int nn)
{
	int ww=weight_01bag+weight[n];
	int vv=value_01bag+value[n];
	//如果选中的质量超过w,就退回 
	if(weight_01bag>w||n>=nn)
		return ;
	//传入就记录最大 
	if(max_value<value_01bag)
		max_value=value_01bag;
	//不选这个物体 
	 get_value(weight,value,n+1,w,weight_01bag,value_01bag,nn);
	//选这个物体 
	 get_value(weight,value,n+1,w,ww,vv,nn);
}

3.动态规划-背包问题(01背包)-正儿八经的动态规划dp表

        有n个物体,质量和价值分别为wi,vi的物品,从这些物品中挑出总重量不超过w的物品,求所有挑选方案中价值总和的最大值。

题目分析:在深搜中会有很多重复的深搜,既然这样我们可以用记忆型递归记录其中的一些结果,在用的时候查询。就没有写那个代码没有跑出来,那就直接上dp表,行是质量递增的序列,列是物品种类,dp表的最下面一个表格就是结果。

//动态规划                                 
//01背包- 正儿八经dp表 
/*
有n个物体,质量和价值分别为wi,vi的物品,
从这些物品中挑出总重量不超过w的物品,
求所有挑选方案中价值总和的最大值 
*/ 
/*
输入
n=4
(w,v)={(2,3),(1,2),(3,4),(2,2)}
w=5

输出:
7(选择第0,1,3号物品) 

因为对每一个物体只有选择和不选择两种情况,所以这个问题称为01背包 
*/
#include<stdio.h>
void get_value(int weight[],int value[],int n,int w);
int max(int a,int b);
int main(void)
{
	int w,n;//w最大承载质量,n物品个数 
	scanf("%d %d",&w,&n);
	int weight[n];
	int value[n];
	for(int i=0;i<n;i++)
	{
		scanf("%d %d",&weight[i],&value[i]);
	 }
	 get_value(weight,value,n,w); 
	return 0;
}
void get_value(int weight[],int value[],int n,int w)
{
	int k=w+1;//若w=5时,就有0~5就有六个元素 
	int arr[n][k];//构建一个dp数组
	//对数组进行初始化
	//先初始化第一行 
	//在初始化第一列 
	for(int i=0;i<k;i++)
	{
		if(weight[0]>i)
			arr[0][i]=0;
		else
			arr[0][i]=value[0];
	 }
	 for(int i=0;i<n;i++)
	 {
	 	arr[i][0]=0;
	  } 
	 //构造dp表
	 for(int i=1;i<n;i++)
	 {
	 	for(int j=1;j<k;j++)
	 	{
	 		//判断i=1这一行的这个质量是否大于j
			//这里的是否就会产生i=1要不要的起这个质量
			//要不起就是上一个区间的最大价值
			//要的起就要减去这个i的质量然后跳转到剩余质量的位置
			//然后找到最大价值
			if(weight[i]>j)//说明要不起
			{
				//要不起就是该列上一行的最大价值
				arr[i][j]=arr[i-1][j]; 
			 } 
			 else//要得起 
			 {
			 	int val_ok;//要的起的价值 
			 	int val_no;//要不起的价值 
				int sy_weight=j-weight[i];
				//要的起的价值=该质量对应的价值+剩余质量对应的价值 
				val_ok=value[i]+arr[i-1][sy_weight];
				//要不起的价值=上一个物品的对应的和这个质量对应的最大价值 
				val_no=arr[i-1][j];
				arr[i][j]=max(val_ok,val_no);	
			  } 
		 }
	  } 
	printf("%d",arr[n-1][k-1]);
}
int max(int a,int b)
{
	if(a>b)
		return a;
	else
		return b;
}

4.动态规划-切割钢条

由于市场需求不同的尺寸有不同的价格,给定一个长为n的钢条和价格表
求切割方案,让收益最大化 

长度i  1  2  3  4  5  6  7  8  9  10
价格pi 1  5  8  16 10 17 17 20 24 30

题目分析:列出dp表,在列表时会发现,在列为5的时候它可以由(0~5)(1~4)(2~3)(2~2)组成,我们需要去查每一个组合的最大值去切割。

//动态规划                                 
//切钢条
/*
由于市场需求不同的尺寸有不同的价格,给定一个长为n的钢条和价格表
求切割方案,让收益最大化 

长度i  1  2  3  4  5  6  7  8  9  10
价格pi 1  5  8  16 10 17 17 20 24 30
*/
#include<stdio.h>
void max_earn(int arr[],int n);
int main(void)
{
	int n=10;
	//加1是为了把0包括进去 
	int arr[n+1]={0,1,5,8,16,10,17,17,20,24,30};
	max_earn(arr,n);
	return 0;
 } 
 void max_earn(int arr[],int n)
 {
 	int max_arr[n+1];
 	//初始化dp表 
 	max_arr[0]=arr[0];
 	max_arr[1]=arr[1];
 	//创建其他dp表 
 	for(int i=2;i<=n;i++)
 	{
 		int max=arr[i];//i=2时,这个就相当于0和2这个情况,其他的1,1交给while去干 
 		int j=0;
 		int k=i;
 		//利用双指针找出最大 
 		while(j<k)
 		{
 			j++;
 			k--;
 			if((max_arr[j]+max_arr[k])>max)
 				max=(max_arr[j]+max_arr[k]);
		 }
		 max_arr[i]=max; 
	 }
	printf("%d ",max_arr[n]); 
 }

5.动态规划-数字三角形(滚动数组(重复利用))

      7
     3 8
    8 1 0
   2 7 4 4
  4 5 2 6 5
以上数字三角形从顶点出发到最底,
每次只能往下的左边或者右边走,
求出走到最底层时数字的累计量最大。 

题目分析:从倒数第一行开始,第一个数据,2有两条路,4或者5,我们就可以在这个局部去选择出最优的路线,当然是2,5,我们就可以把2+5的值赋给2,因为2不会在被使用到,过了就是7,和2同理

//动态规划                                 
//数字三角形-滚动数组(重复利用) 
/*
      7
     3 8
    8 1 0
   2 7 4 4
  4 5 2 6 5
以上数字三角形从顶点出发到最底,
每次只能往下的左边或者右边走,
求出走到最底层时数字的累计量最大。 
*/
#include<stdio.h>
void get_max_num(int arr[][5],int n);
int max(int a,int b);
int main(void)
{
	int n=5;
	int arr[][5]={
	{7},
	{3,8},
	{8,1,0},
	{2,7,4,4},
	{4,5,2,6,5}
	};
	get_max_num(arr,5);
	return 0;
 } 
 void get_max_num(int arr[][5],int n)
 {
 	int arr_temp[5]={4,5,2,6,5};
 	for(int i=n-2;i>=0;i--)//为什么是n-2呢?因为我们到从倒数第二行开始,且第二行的下标就需要减1
	{
		for(int j=0;j<=i;j++)
		{
			arr_temp[j]=max(arr[i][j]+arr_temp[j],arr[i][j]+arr_temp[j+1]);
		}
	 } 
	printf("%d",arr_temp[0]); 
 }
 int max(int a,int b)
 {
 	if(a>b)
 	return a;
 	else
 	return b;
 }

6.动态规划-最大公共子序列

有如下案例,输出的结果为最大公共子序列

AB34C
A1BC2
结果为ABC 
3563243
513141
结果为534 

题目分析:列dp表即可,对应元素如果相同的就在i-1,j-1这个位置的值加一,不相同就在该表格上面或者左边的最大值

//动态规划                                 
//最大公共子序列问题
/*
AB34C
A1BC2
结果为ABC 
3563243
513141
结果为534 
*/ 
#include<stdio.h>
#include<string.h>
void get_biggest_public_seq(char str1[],char str2[]);
int max(int a,int b);
int main(void)
{
	char str1[100];
	char str2[100];
	gets(str1);
	gets(str2);
	get_biggest_public_seq(str1,str2);
	return 0;
 } 
 //建立dp表
 void get_biggest_public_seq(char str1[],char str2[])
 {
 	//计算出两个字符串的长度,str1为列,str2为行 
 	int leng_str1=strlen(str1);
	int leng_str2=strlen(str2);
	
	//以字符串str1的长度为列的长度 
	//以字符串str2的长度为行的长度 
	int dp[leng_str2][leng_str1];
	
	//先初始化第一行第一列
	//初始化第一行 
	int log=0;
	for(int i=0;i<leng_str1;i++)
	{
		if(str1[i]==str2[0]||log==0)//找到第一个相同,其他相同的 
			log=1;
		if(log==0)
			dp[0][i]=0;
		 else
		 	dp[0][i]=1;
	 } 
	 //初始化第一列
	log=0;//重新初始化标志位 
	for(int i=0;i<leng_str2;i++)
	{
		if(str2[i]==str1[0]||log==0)//找到第一个相同,其他相同的 
			log=1;
		if(log==0)
			dp[i][0]=0;
		 else
		 	dp[i][0]=1;
	 }  
	 //构建dp表的其他表格 
	for(int i=1;i<leng_str2;i++)//以str2作为行 
	{
		for(int j=1;j<leng_str1;j++)//以str1作为列 
		{
			if(str2[i]==str1[j])
				dp[i][j]=dp[i-1][j-1]+1;
			else
				dp[i][j]=max(dp[i-1][j],dp[i][j-1]); 
		}
		
	 } 
	 
	printf("%d",dp[leng_str2-1][leng_str1-1]);
 }
 int max(int a,int b)
 {
 	if(a>b)
 		return a;
 	else
 		return b;
 }

7.动态规划-背包问题(完全背包问题)

有n个质量和价值分别为wi,vi的物品,从这些物品中挑选出重量不超过w的物品,
求所有挑选的方案中价值总和的最大值
强调:同一种物品可以拿多个 

题目分析:重点是同一种物品可以拿多次,那么我们在建立dp表的时候这个物品那一次的价值,那两次的价值,在把剩余的去查dp表,看哪一次我们拿的价值最大就怎么样去拿,

//动态规划                                 
//完全背包问题
/*
有n个质量和价值分别为wi,vi的物品,从这些物品中挑选出重量不超过w的物品,
求所有挑选的方案中价值总和的最大值
强调:同一种物品可以拿多个 

案例输入
 n=4
 (w,v)={(2,3),(1,2),(3,4),(2,2)} 
 w=5 
4 5
2 3
1 2
3 4
2 2
*/
#include<stdio.h>
void completely_bag_question(int weight[],int value[],int n,int w);
int max(int a,int b);
int main(void)
{
	//定义参数,获取参数 
	int n,w;
	scanf("%d %d",&n,&w);
	int weight[n];
	int value[n];
	for(int i=0;i<n;i++)
	{
		scanf("%d %d",&weight[i],&value[i]);
	 } 
	completely_bag_question(weight,value,n,w);
	return 0;
}
void completely_bag_question(int weight[],int value[],int n,int w)
{
	//定义一个二维的dp表,列为0~n的载重,行为每个物品的质量
	int k=w+1;
	int dp[n][k];//为什么加1呢?因为0~w有w+1个
	
	//初始化行和列
	//初始化第一列
	for(int i=0;i<n;i++)
	{
		//因为质量为zero所以一个价值也没有 
		dp[i][0]=0;
	 } 
	//初始化第一列
	for(int i=1;i<k;i++)
	{
		if(i<weight[0])
			dp[0][i]=0;
		else
			dp[0][i]=i/weight[0]*value[0];//这里i/weight[0]说明i可以取到多少这这样质量的 
	 } 
	 
	 //构建其他dp表
	 for(int i=1;i<n;i++)
	 {
	 	for(int j=1;j<k;j++)
	 	{
	 		//然后去抓起0个抓取1个抓取2个等等
	 		//由于抓取0个以后还是剩下原本的质量
			//所以我们这里先定义一个max,先把他初始化为抓取零个的情况,就是不抓这个物品,相当于只有上一个范围 
			int max_log=dp[i-1][j];
			 for(int a=1;a*weight[i]<=j;a++)
			 {
			 	//先抓,然后在把剩下的去查dp表得出max,然后在进行比较
				int shenxia=j-a*weight[i];
				int max_temp=a*value[i]+dp[i-1][shenxia];
				max_log=max(max_log,max_temp);
			 }
			 dp[i][j]=max_log;
		 }
	  }
	printf("%d",dp[n-1][k-1]);
}
int max(int a,int b)
{
	if(a>b)
		return a;
	else
		return b;
}

8.动态规划-最长递增子序列

输入4 2 3 1 5 6
输出4 (因为2 3 5 6组成了最长递增子序列) 

像这样的最长递增子序列

题目分析:这个题建立的dp表是一维的,他表示在以这个下标为末尾的最长的递增子序列

//动态规划                                 
//最长递增子序列
/*
输入4 2 3 1 5 6
输出4 (因为2 3 5组成了最长递增子序列) 
*/ 
#include<stdio.h>
void get_longest_inc_seq(int str[],int n);
int max(int a,int b);
int main(void)
{
	int n;
	scanf("%d",&n);
	int str[n];
	for(int i=0;i<n;i++)
	{
		scanf("%d",&str[i]);
	}
	get_longest_inc_seq(str,n);
	return 0;
 } 
void get_longest_inc_seq(int str[],int n)
{
	//创建dp数组,dp数组的值表示包含下标位置的最长递增子序列 
	int dp[n];
	//初始化dp[0] 
	dp[0]=1;
	for(int i=1;i<n;i++)//这样扫描原始数组的每一个元素,不包含下标为zero的 
	{
		int max_temp=0;//这个变量为i=1这个位置需要填写的值 
		//从这个下标开始去扫描dp数组里的每一个元素对,去维持一个最大的max_temp
		for(int j=0;j<i;j++)
		{
			int maxtemp;//通过这个变量可以得到最大的值 
			if(str[i]>str[j])
			{
				 maxtemp=dp[j]+1;
				 max_temp=max(max_temp,maxtemp);
			}
			else
			{
				maxtemp=dp[j];
				max_temp=max(max_temp,maxtemp);
			}
		 } 
		 dp[i]=max_temp;
	 } 
	 int max=0;
	 for(int i=0;i<n;i++)
	 {
	 	if(dp[i]>max)
	 		max=dp[i];
	 }
	 printf("%d",max);
}
int max(int a,int b)
{
	if(a>b)
		return a;
	else
		return b;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值