动态规划

一、相关术语

  1. 阶段:把问题求解分为若干阶段。
  2. 状态:每个阶段的状态。
  3. 转移方程:eg:max{f(i),f(i+1)}
  4. 边界:初始条件
  5. 最优子结构
  6. 无后效性:过去不会影响未来,现在才会影响未来(马尔科夫决策)
  7. 重叠子问题:重复出现的以求解却需在求解问题

二、是否可以用动态规划

  1. 计数:有多少种方式?
  2. 求最大/多/小/少:coin change
  3. 求存在性:能否选出k个数之和为sum

三、动态规划思想

核心:把最优解分解为若干子问题求解,对子问题继续分解。即对问题进行递推(借助转移方程:可以说是递归方程的另一种变形)。详见下列问题。

1.fibonacci squence(斐波那契数列)

在这里插入图片描述

//递归:时间复杂度为O(2^n)
int fib1(int n)
{
    if(n==1||n==2)
        return 1;
    else
        return fib1(n-1)+fib1(n-2);
}
//动态规划思想:时间复杂度为O(n)
int fib2(int n)
{
    int s,x,y;
    x=y=1;
    if(n==1||n==2)
        return 1;
    for(int i=3;i<=n;i++)
    {
        s=x+y;
        x=y;
        y=s;
    }
}

2.时间Time

在这里插入图片描述

3.选不相邻数相加最大

在这里插入图片描述

#include <iostream>
using namespace std;
//递归
int rec_opt(int a[],int n)
{
    if(n==1)
        return a[0];
    if(n==2)
        return max(a[0],a[1]);
    else
    {
        int b,c;
        b=rec_opt(a,n-2)+a[n-1];
        c=rec_opt(a,n-1);
        return max(b,c);
    }
}
//动态规划思想
int dp_opt(int a[],int n)
{
    int opt[n];
    int i;
    if(n==1)
        return a[0];
    if(n==2)
        return max(a[0],a[1]);
    opt[0]=a[0];
    opt[1]=max(a[0],a[1]);
    for(i=2;i<n;i++)
    {
        opt[i]=max(opt[i-2]+a[i],opt[i-1]);
    }
    return opt[i-1];
}
int main()
{
    int arr[7]={6,10,3,9,6,1,7};
    int sum;
    //sum=rec_opt(arr,7);
    sum=dp_opt(arr,7);
    cout<<sum;
    return 0;
}

4.求存在性

在这里插入图片描述

#include <iostream>
using namespace std;
//递归
bool rec_subset(int a[],int i,int s)
{
    if(s==0)
        return true;
    else if(i==0)
        return a[0]==s;
    else
    {
        bool x,y;
        x=rec_subset(a,i-1,s-a[i]);
        y=rec_subset(a,i-1,s);
        return x||y;
    }
}
//动态规划
bool dp_subset(int a[],int n,int s)
{
    bool opt[n][s];
    for(int i=0;i<n;i++)
        opt[i][0]=true;
    for(int i=0;i<=s;i++)
        opt[0][i]=false;
    opt[0][a[0]]=true;
    for(int i=1;i<n;i++)
        for(int j=1;j<=s;j++)
    {
        if(a[i]>j)
            opt[i][j]=opt[i-1][j];
        else
        {
            opt[i][j]=opt[i-1][j]||opt[i-1][j-a[i]];
        }
    }
    return opt[n-1][s];
}
int main()
{
    int arr[6]={3,34,4,12,5,2};
    bool s;
    //s=rec_subset(arr,5,13);
    s=dp_subset(arr,6,13);
    cout<<boolalpha<<s;
    return 0;
}

5.硬币问题

有1元、5元、10元、50元、100元、500元的硬币各C1,C5,C10,C50,C100,C500枚。现在要用这些硬币来支付A元,最少需要多少枚硬币?

对于硬币问题:可以使用贪心算法,但是贪心算法并不一定算出来的就是最少硬币数,只是接近最优解。动态规划一定能算出最优解。eg:如果有10,7,5,1这四种类型的硬币,要付12元,按照贪心算法会为10+1+1,需要3枚,实际最少是7+5,2枚就够了。

#include <iostream>
using namespace std;
//贪心算法
int tanxin(int a[],int n,int s)
{
    int sum=0;
    for(int i=n-1;i>=0;i--)
    {
        int t=s/a[i];
        s=s-t*a[i];
        sum+=t;
    }
    return sum;
}
//动态规划
int dp_opt(int a[],int n,int s)
{
    int opt[s+1];
    opt[0]=0;
    opt[1]=1;
    opt[5]=1;
    opt[7]=1;
    opt[10]=1;
    for(int i=2;i<s+1;i++)
    {
        int x,y,z,w,m1,m2;
        if(i>10)
        {
            x=opt[i-10]+1;
            y=opt[i-7]+1;
            z=opt[i-5]+1;
            w=opt[i-1]+1;
            m1=min(x,y);
            m2=min(z,w);
            opt[i]=min(m1,m2);
        }
        else if(i>7&&i!=10)
        {
            x=opt[i-7]+1;
            y=opt[i-5]+1;
            z=opt[i-1]+1;
            m1=min(x,y);
            opt[i]=min(m1,z);
        }
        else if(i>5&&i!=7&&i!=10)
        {
            x=opt[i-5]+1;
            y=opt[i-1]+1;
            opt[i]=min(x,y);
        }
        else if(i>1&&i!=5&&i!=7&&i!=10)
        {
            opt[i]=opt[i-1]+1;
        }
    }
    return opt[s-3];
}
int main()
{
    int arr[4]={1,5,7,10};
    int s=tanxin(arr,4,12);
    int s1=dp_opt(arr,4,12);
    cout<<s;
    cout<<s1;
    return 0;
}

6.背包问题

一个旅行者有一个最多能装 M 公斤的背包,现在有 n 件物品,它们的重量分别是W1,W2,…,Wn,它们的价值分别为C1,C2,…,Cn,求旅行者能获得最大总价值。
输入
第一行:两个整数,M(背包容量,M≤200)和N(物品数量,N≤30);

第2…N+1行:每行二个整数Wi,Ci,表示每个物品的重量和价值。

输出
仅一行,一个数,表示最大总价值。
样例输入
10 4
2 1
3 3
4 5
7 9

在这里插入图片描述

#include <iostream>
using namespace std;
typedef struct
{
    int c;
    int w;
}node;
//背包问题的递归算法
int rec_value(node arr[],int n,int m)
{
    if(m==0)
        return 0;
    else if(n==0)
    {
        if(arr[0].w>m)
            return 0;
        else
            return arr[0].c;
    }
    else if(arr[n].w>m)
        return rec_value(arr,n-1,m);
    else
    {
        int a,b;
        a=rec_value(arr,n-1,m);
        b=rec_value(arr,n-1,m-arr[n].w)+arr[n].c;
        return max(a,b);
    }
}
//动态规划
int dp_value(node arr[],int n,int m)
{
    int dp[n+1][m+1];
    for(int i=0;i<=n;i++)
        dp[i][0]=0;
    for(int j=0;j<=m;j++)
        dp[0][j]=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
    {
        if(arr[i-1].w>j)
            dp[i][j]=dp[i-1][j];
        else
        {
            dp[i][j]=max(dp[i-1][j],dp[i-1][j-arr[i-1].w]+arr[i-1].c);
        }
    }

    return dp[n][m];
}
int main()
{
    int m,n;
    node arr[30];
    cin>>m>>n;
    for(int i=0;i<n;i++)
        cin>>arr[i].w>>arr[i].c;
    //int maxvalue=rec_value(arr,n-1,m);
    int maxvalue=dp_value(arr,n,m);
    cout<<maxvalue;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值