OJ每日一练试水第5天,聊聊动态规划的问题

时间:2019年7月5日

Dynamic Programming

动态规划和分治法差不多
性质有
1.最优子结构 Optimal Substructure
2.重复子问题 overlap sub-problem(通过保存数据到一张表里 然后再次需要计算这个数据可以通过直接查表的形式)
唯一的区别就是dp所拆成的子问题不是相互独立的
同时每个子问题的答案都会保存下来
同时对于有些问题 dp也与传统的递归和回溯不一样 因为递归需要很多次重复的计算 耗费大量的时间 效率低

国外大神通过一些网页形式来反映一些算法:http://karaffeltut.com

knapsack

下面是最简单的01背包问题
在这里插入图片描述
题目描述 小偷带了一个容量为M(20)的包,然后小偷怎么偷上述东西使得价值最高

递推公式: K也可以理解还能偷几样东西(背包里还能放几样东西)
在这里插入图片描述
最终的01背包形式 所以此题的最优解为26
http://karaffeltut.com/NEWKaraffeltutCom/Knapsack/knapsack.html
DP表:
在这里插入图片描述

#include<stdio.h>
#define N 6
#define M 21

int B[N][M]={0};//保存每种背包形式的结果 
int v[N]={0,3,4,5,8,10}; 
int w[N]={0,2,3,4,5,9}; 

//对于这种问题 要学好数学的递推公式 然后再抽象成程序语言   最简单的背包问题的算法 
void knapsack()
{
	int k,c;
	for(k=1;k<N;k++){
		for(c=1;c<M;c++){
			if(w[k]>c)//当前的商品重量大于了背包容量 
			{
				B[k][c]=B[k-1][c];  //c表示当前背包的剩余容量 ,k表示还可以偷的个数 
			}else{
				int v1=B[k-1][c-w[k]]+v[k];   //偷的话 
				int v2=B[k-1][c];    //不偷的话
				if(v1>v2){
					B[k][c]=v1;
				}else{
					B[k][c]=v2;
				}				
			}
		}
	}
}
int main()
{
	knapsack(); 
	printf("***%d****\n",B[5][20]);
	return 0; 
} 

对于此类问题
求出递推公式真的很重要吧

Longest common subsequence–LCS

LCS问题的递推公式
在这里插入图片描述
c[i,j]表示:(x1,x2…xi) 和 (y1,y2…yj) 的最长公共子序列的长度。(是长度哦,就是一个整数嘛)
详情可以去看这篇博客 写的非常棒 很好理解
https://www.cnblogs.com/wkfvawl/p/9362287.html

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1000;
char a[N],b[N];
int dp[N][N];
int main()
{
    int lena,lenb,i,j;
    while(scanf("%s%s",a,b)!=EOF)
    {
        memset(dp,0,sizeof(dp));
        lena=strlen(a);
        lenb=strlen(b);
        for(i=1;i<=lena;i++)
        {
            for(j=1;j<=lenb;j++)
            {
                if(a[i-1]==b[j-1])
                {
                    dp[i][j]=dp[i-1][j-1]+1;
                }
                else
                {
                    dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
                }
            }
        }
        printf("%d\n",dp[lena][lenb]);
    }
    return 0;
}

矩阵连乘

在这里插入图片描述
对于完全加括号的矩阵连乘
矩阵A:P * Q
矩阵B:Q * R
则两个矩阵的总次数为 P * Q * R次
在这里插入图片描述
https://www.cnblogs.com/scarecrow-blog/p/3712580.html

经典例题 加权项目时间计划

在指定的时间内使得收益最高
在这里插入图片描述

OPT表示最优解
递推式
在这里插入图片描述
在这里插入图片描述

例题二

在这里插入图片描述
题目描述:给如上的一组数据从中挑出来的数使得和最大(且挑选的数不能相邻 如 1 2 4就不行 1 4 7就ok),挑的数不限量

分析:改题目中分解的子问题中存在重复项,故用dp效率更高
在这里插入图片描述
递推式:
在这里插入图片描述
出口:
在这里插入图片描述

#include<stdio.h>
#include<math.h>
int arr[]={1,2,4,1,7,8,3};
int opt[6];
int n=6;
int max(int a,int b)
{
	return a>b?a:b;
}
//递归实现 超级的慢 时间复杂度是2的n次方 
int rec_opt(int n)
{
	if(n==0)
	{
		return arr[0];
	}else if(n==1)
	{
		return max(arr[0],arr[1]);
	}else{
		return max(rec_opt(n-2)+arr[n],rec_opt(n-1));
	}
}
//动态规划实现 
int dp_opt(int arr[])
{
	opt[0]=arr[0];
	opt[1]=max(arr[0],arr[1]);
	for(int i=2;i<=n;i++)
	{
		opt[i]=max(opt[i-2]+arr[i],opt[n-1]);	
	}
	return opt[n];
}
int main()
{
	int sum=rec_opt(n);
	printf("%d\n",sum);
	int sum2=dp_opt(arr);
	printf("%d\n",sum2);
	return 0;
} 

Python代码:

arr=[1,2,4,1,7,8,3]

def rec_opt(arr,i):
    if i==0 :
        return arr[0]
    if i==1 :
        return max(arr[0],arr[1])
    else :
        A = rec_opt(arr,i-2)+arr[i]
        B = rec_opt(arr,i-1)
        return max(A,B)
    
def dp_opt(arr):
    opt = [0]*len(arr)
    opt[0]=arr[0]
    opt[1]=max(arr[0],arr[1])
    for i in range(2,len(arr)):
        A = opt[i-2]+arr[i]
        B = opt[i-1]
        opt[i]=max(A,B)
    return opt[len(arr)-1]
print(dp_opt(arr))

输出的结果为:
在这里插入图片描述

例题三

在这里插入图片描述
题目描述:给定一个数组从中任意的选取值,使得值的和为S,如果可以返回True 否则返回false

分析:建立子集Subset(arr[i],s)其中i表示当前的位置,s表示求的和
例如subset(arr[5],9)就表示
在这里插入图片描述
递推式:
在这里插入图片描述
出口:
在这里插入图片描述
DP表:
在这里插入图片描述

#include<stdio.h>
#include<math.h>
int arr[]={3,34,4,12,5,2};
int len=6;
int s=9;
bool dp_subset(int arr[],int s)
{
	bool subset[len][s+1];
	int i,j;
	for(i=0;i<len;i++)
		for(j=0;j<s+1;j++)
		subset[i][j]=false;
	for(i=0;i<len;i++) //第一列全为T
		subset[i][0]=true;
 	for(j=0;j<s+1;j++) //第一行全为F
 		subset[0][j]=false;
	for(i=1;i<len;i++)
		for(j=1;j<s+1;j++)
		{
			if (arr[i]>s){
				subset[i][j]=subset[i-1][j];  
			}else
            {
                subset[i][j]= (subset[i-1][j]) || (subset[i-1][j-arr[i]]) ;
            }    
		}
	return subset[len-1][s];
}
int main()
{
	
	bool ans=dp_subset(arr,s);
	if(ans){
		printf("True");
	}else{
		printf("False");
	}
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

黑瞳丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值