动态规划算法经典例题_c动态规划精简例题

相信很多初学者,在刚刚接触动态规划,都花费了不少时间。网上有很多人写如何去做动态规划的题目,但是很少有整理适合初学者做的习题。这里是我自己考研期间,复习整理的。希望对大家有帮助。

c动态规划精简例题

1. 单序列型

  1. 爬楼梯
算法思想:
         1、定边界体条件,因为有两种走法,所以dp[0]=cost[0],dp[1]=cost[1]
         2、我们假设第i个为最后一步,则有两种情况
            min{dp[i-1],dp[i-2]}+sonst[i]
//1.爬楼梯

int minCostClimbingStairs(int cost[], int n)
{
    int dp[n];
    dp[0] = cost[0];
    dp[1] = cost[1];
    for (int i = 2; i < n; i++)
    {
        dp[i] = min{dp[i - 1], dp[i - 2]} + cost[i];
    }
    return min{dp[n - 2], dp[n - 1]};
}
  1. House Robber
算法思想:
    1、边界条件:如果没有房子,则return 0;
    2、如果只有一个房子,则返回这一个房子
    3、dp[0]=nums[0];
       dp[1]=max{nums[0],nums[1]};
    4、比较第i个房子偷(dp[i - 2] + nums[i])和不偷(dp[i - 1])的时候的大小
//2.House Robber

int rob(int nums[], int n)
{
    if (n == 0)
        return 0;
    if (n == 1)
        return nums[0];
    int dp[n];
    dp[0] = nums[0];
    dp[1] = max{nums[0], nums[1]};
    for (int i = 2; i < n; i++)
    {

        dp[i] = max(dp[i - 1], dp[i - 2] + nums[i]);
    }
    return dp[n - 1];
}
  1. 最长上升子序列
算法思想:这里用到的是一个自底向上的寻找最优子结构的的思想。粗俗来说:如果你想要得到七个数里面的最长子序列,你可以先找前6个数里面的最长子序列,同理,你又必须得找前5个数里面的最长子序列,直到子序列为1
    1、我们先令dp[i]=1;//初始化,假如刚开始前面没有比它小的元素只有它自己
    2、我们在0~i之间,与dp[i]进行比较,如果,比dp[i]大,则dp[i]=dp[k]+1
    3、一趟内for循环结束,确定一个空,这个时候和max进行比较。因为最长上升子序列不一定是最后一个,所以每次都要比较下
//3.最长上升子序列

int lengthOfLIS(int nums[], int n)
{
    if (n == 0)
        return 0;
    int dp[n];
    dp[0] = 1;
    max = 1;
    for (int i = 1; i < n; i++)
    {
        dp[i] = 1;//一开始最长上升子序列只有自己1个
        for (int k = 0; k < i; k++)
        {
            if (nums[k] < nums[i])//与比nums[i]小的之前的所有的最长上升子序列进行比较
                dp[i] = max{dp[i], dp[k] + 1};
        }
        max = max{max, dp[i]};//最长上升子序列不一定是最后一个,所以每次都要比较下
    }
    return max;
}
  1. (模拟卷上的例题)

8d2c882c1e3a5fd5c4346a69bcdc9afb.png

.JPG)

算法思想:
    1、我们用一个一维数组来统计输入的数据的个数
    2、我们a来存储最大值,作为后面for循环的终止条件
    3、我们每次到i的时候都是假设i为最后一个元素,与前面的状态进行比较
           a.如果第i个数据我们不选择,则dp[i-1]
           b.如果我们选择,则dp[i-2]+vis[i]*i
    4、找里面最大值
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
define N 1000 int main()
{
    int n, a, m = 0;
    int dp[N];   //dp[i]为数字1~i能获得的最大价值
    int vis[dp]; //用来统计序列中数字i的个数
    memset(vis, 0, sizof(vis));
    scanf("%d", &n);
    for (int i = 0; i < n; i++)
    {
        scanf("%d", &a);
        vis[a]++;
        m = max(m, a);
    }
    dp[0] = 0;
    dp[1] = vis[1];
    for (int i = 2; i < m; i++)
    {
        dp[i] = max{dp[i - 1], dp[i - 2] + vis[i] * i};
    }
    printf("%dn", dp[m]);
    return 0;
}

2.双序列型

  1. 最长公共子序列

转移方程: $$ dpleft[ i right] left[ j right] =begin{cases} 0 & & {i=0text{或}j=0} dpleft[ i-1 right] left[ j-1 right] +1 & & {Aleft[ i right] =Bleft[ j right]} 0 & & {Aleft[ i right] ne Bleft[ j right]} end{cases} $$

核心算法:
    if (A[i] == B[j]) //如果最后一个元素相同
        dp[i][j] = dp[i - 1][j - 1] + 1;
    else
        dp[i][j] = max{dp[i - 1][j], dp[i][j - 1]};
void LCSLength(int m, int n, char *x, char *y, int dp[][])
{
    int i, j;
    for (i = 1; i <= m; i++)
        dp[i][0] = 0;
    for (i = 1; i <= n; i++)
        dp[0][i] = 0;
    for (i = 1; i <= m; i++)
    {
        for (j = 1; j <= n; j++)
        {
            if (x[i] == y[j])
                dp[i][j] = dp[i - 1][j - 1] + 1;
            else
            {
                dp[i][j] = max{dp[i - 1][j], dp[i][j - 1]};
            }
        }
    }
    return dp[m][n];
}
  1. Edit Distance
int minDistance(string word1, string word2)
{
    int n = word1.length;
    int m = word2.length;

    int dp[n][m];
    //边界条件
    for (int i = 0; i < n + 1; i++)
        dp[i][0] = i;
    for (int j = 0; j > m + 1; j++)
        dp[0][j] = j;

    for (int i = 0; i < n + 1; i++)
    {
        for (int j = 0; j < m + 1; j++)
        {
            dp[i][j] = min{dp[i - 1][j], dp[i][j - 1]} + 1; //delet和insert中找最小的
            if (word1.charAt(i - 1) == word2.charAt(j - 1))
            {
                dp[i][j] = min{dp[i][j], dp[i - 1][j - 1]};//如果最后一个元素相同,则找前一个和这个,谁最小
            }
            else
            {
                dp[i][j] = min{dp[i], dp[i - 1][j - 1] + 1};//如果不相同,则和replace进行比较
            }
        }
    }
}

![IMG_1504(20201127-154444)](C:Users渐明DocumentsTencent Files834422139FileRecvMobileFileIMG_1504(20201127-154444).JPG)

算法思想:
        用dp[i][0/1/2]分别表示第i天选择休息/健身/上网比赛的最少休息天数,因为不能连续两天做同样的事情,所以今天选择健身的话,从昨天选择了休息或者上网比赛的状态进行转移
#include <algorithm>
#include <stdio.h>
#include <stdlib.h>
using namespace std;

const int INF = 10000;
const int mx = 110;
int dp[mx][3];

int main()
{
    int n, a;
    scanf("%d", &n);
    memset(dp, INF, sizeof(dp));
    //dp[i][0/1/2] rest/gym/match
    dp[0][0] = 0;
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &a);
        int minn = min(dp[i - 1][1], dp[i - 1][2]);
        dp[i][0] = min(dp[i - 1][0], minn) + 1;
        if (!a)
            continue;
        if (a != 2) //choose to join match
        {
            dp[i][2] = min(dp[i - 1][0], dp[i - 1][1]);
        }
        if (a != 1) //choose to gym
        {
            dp[i][1] = min(dp[i - 1][0], dp[i - 1][2]);
        }
    }
    printf("%dn", min(dp[n][0], min(dp[n][1], dp[n][2])));
    return 0;
}

3. 0-1背包问题

0-1背包的意思是:要么拿,要么不拿;拿只能拿一整个,不存在只是拿部分;物品只有一个,拿了之后就没了
//问题描述:
Input:
n items
v[i]: ith item 的价值
w[i]: ith item 的重量
C:knapsack 的最大承重量
Note: at most one copy of each item
Output: 能带走的最大价值总和
//问题分析:

f(i,j)=max{f(i+1,j),f(i+1,j-w[i])+v[i]}  //第i个物品不选,第i个物品选,从中选出最大的一项出来

Base Case:  f(0,j)=0   //房子内没有物品
            f(i,0)=0   //没有带袋子/或者是袋子已经满了
public int knapsack(int &v,int &w,int C)
{
    int n=v.length;
    int[][] dp=new int[n+1][C+1];
    for(int r=1;r<=n;r++)
    {
       for(int c=1;c<=C;c++)
        {
           dp[r][c]=dp[r-1][c];    //表示未放入这个商品之前,最优解
            if(w[r-1]<=c)
            {
                dp[r][c]=Math.max(dp[r][c],dp[r-1][c-w[r-1]]+v[r-1]);
            }
        } 
    }        
}

4. 完全背包问题

完全背包是在N种物品中选取若干件( 同一种物品可多次选取)放在空间为V的背包里,每种物品的体积为C1,C2,…,Cn,与之相对应的价值为W1,W2,…,Wn.求解怎么装物品可使背包里物品总价值最大。

状态转移方程: $$ dpleft[ i right] left[ j right] =max left{ dpleft[ i-1 right] left[ j-kcdot vleft[ i right] right] +kcdot vleft[ i right] right} ,, 0leqslant k&kcdot wleft[ i right] ll j $$

//问题描述:
Input:
      n items
      v[i]: ith item的价值
      w[i]: ith item的重量
      C:Knapsack的最大承重重量
      Note: infinite copies of each item(每一种物品不限制数量)
      Output: 能带走的最大价值总和
#include <iostream>
#include <string.h>
using namespace std;
#define Max_n 105
#define Max_w 10005

int n,W;
int w[Max_n],v[Max_n];
int dp[Max_n][Max_w];

//初始化
void initialize(int n,int m)
{
    for(int i=0; i<=n; i++)
        dp[i][0]=0;
    for(int j=0; j<=W; j++)
        dp[0][j]=0;
}

//dp[i][j]=max⁡{dp[i-1][j-k*w[i]]+k*v[i]|0≤k&k*w[i]≤j}
void solve_1()
{
    for(int i=1; i<=n; i++)
        for(int j=1; j<=W; j++)
        {
            dp[i][j]=-1;
            for(int k=0; k*w[i]<=j; k++)
                dp[i][j]=max(dp[i][j],dp[i-1][j-k*w[i]]+k*v[i]);
        }
}

注:完全背包和0-1背包的两个for循环的位置代表的东西不同,主要是由于i用来表示每一种物品。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值