动态规划算法题总结

可能是没有理解透动态规划的思想吧
动态规划真的是做的我......难受
得对照着百度的思路来实现,而且做完一道题觉得...嗯...挺简单的,思路也挺清晰的
再做一道,题型稍微一变化...又得想半天想不出来然后百度思路...
所以总结一下吧...也算是再次理解一遍了


第一道:数字塔问题:
要求从顶层走到底层,若每一步只能走到相邻的结点,则经过的结点的数字之和最大是多少?

[外链图片转存失败(img-taMECG7e-1564492677914)(https://odzkskevi.qnssl.com/79b65092734d6a263a9280f5b332c5b9?v=1501552325)]
输入
输入的数据首先包含一个整数 C,接下来是 C 组数据。每组数据首先是一个
正整数 N (1 ≤ N ≤ 100),表示数字之塔的高度,然后是 N 行数字,其中
第 i 行数字有 i 个以空格作为分隔符的小于 100 的非负整数。
输出
输出离开时你需要说出的数字 K,每组数据的输出占一行。

可以说这道题还是比较简单的了,而且我也有一个比较容易实现的思路
就是从最后一层往上一层一层的加数字
且听我慢慢道来:

  • 我们从最后一层开始往上走,我们有8种选择,即19-2,7-2,7-18,10-18 等等,但是我们不知道哪一条路最终是最大值,不如全部走一遍吧。
  • 但是有的路是不用走的,因为我们可以看到,每个上一层都有两条路可以选,即19、7都通往的是2,那既然不管走19还是7都会通向2,也就是说选19或者7都不会影响到以后的路,因为已经是19、7已经是最后一层了。
    也可以这样想,假设我们已经知道了最优解,我们按照最优解从上往下走刚好是到了2这里,那么最后一层只能选择19或者7,那当然选择19啦,因为要最大值啊。
  • 那么现在我们就可以,把19的值加到2上去,这样我们只要走到2就可以不用往下走了,因为已经把2这里的最优解算出来了。
  • 那既然这样,按照同样的方法,我们把倒数第二层的最优解全部找出来,现在倒数第二层就依次是:2+19,18+10,9+10,5+16.
  • 那再同样的办法把倒数第3层最优解找出来,接着是倒数第4层
  • 到了倒数第5层发现只有一个9和倒数下一层的两个最优解,那么选择一个最大的加上就好啦

整个过程我们只需要把塔变为二维数组,然后从倒数第二层开始到第一层,依次遍历一下数组,把这层数
组的下一层的两条路选择最大的一个加上来,然后遍历完输出第一层的数字就可以了。
代码:

#include<stdio.h>
int main()
{
  int n,N;
  scanf("%d",&N);       //输入你想要测试几组数据
  while(N--)
  {
    scanf("%d",&n);        //输入塔的高度
    int a[n][n];
    int i,j,max;
    for(i=0;i<n;i++)
    {
      for(j=0;j<=i;j++)
      {
        scanf("%d",&a[i][j]);      //依次输入塔每一层的值
      }
    }

    for(i=(n-2);i>=0;i--)              //开始遍历,从塔的倒数第二层开始遍历
    {
      for(j=0;j<=i;j++)
      {
        if(a[i+1][j]>a[i+1][j+1])
        {
          max=a[i+1][j];
        }
        else
        {
          max=a[i+1][j+1];
        }
        a[i][j]=a[i][j]+max;
      }
    }
    printf("%d\n",a[0][0]);
  }
}


第二道:兑换硬币
在一个国家仅有1分,2分,3分硬币,将钱N兑换成硬币有很多种兑法。请你编程序计算出共有多少种兑法。
输入
每行只有一个正整数N,N小于32768。
输出
对应每个输入,输出兑换方法数。

这道题应该是5道题里最简单的一道了,哈哈~
简单的分情况...我用的方法跟动态规划关系不大。emmmmmm

  • 如果将3分硬币确定下来了,那么在这个固定的3分硬币个数下,兑换方法数也就知道了
  • 总钱数减去已经换到的3分硬币的钱数,剩下的钱可以兑换一个两分硬币、两个两分硬币、三个两分硬币...直到换完,换一个的时候剩余的钱直接全变为1分硬币就可以了,以此类推。
  • 所以,换完3分硬币后,剩下的钱可以换多少种2分硬币就是这种情况下所有的可能。

这道题很简单,我就不多说了
代码:

#include<stdio.h>
int main()
{
  int n,i,j,k;
  while(scanf("%d",&n)!=EOF)        //输入总钱数
  {
    k=0;
    for(i=0;3*i<=n;i++)            //遍历可以换到的3分硬币个数
    {
      k=k+(n-3*i)/2+1;              //剩余钱数除2就是可以换的2分硬币个数,+1就是两分硬币一个都不换的情况
    }
    printf("%d\n",k);
  }
}

第三道:超级跳跃
《超级跳跃》是一款非常简单的小游戏,它的规则是这样的:
a. 游戏赛道被分为了 N 块区域,每块区域具有一个价值 K i ;
b. 玩家起始站在道路的起点处,当参与者到达终点处时游戏结束;
c. 玩家每次可以选择使用超级跳跃到达前方的任意区域,但到达的区域
必须满足以下两个条件之一:1 到达的区域是终点;2 到达的区域
的价值大于当前所在的区域价值。
d. 玩家每到达一个区域,就获得这块区域的价值。
e. 不可以使用超级跳跃向身后跳。
请你编写一个程序,算出对于一条《超级跳跃》游戏的赛道,玩家最多可以
获得多少价值。
输入
输入的数据有多组,每组数据首先是一个正整数 N (0 ≤ N ≤ 1000),代表
赛道被分为 N 块区域,接下来是 N 个以空格为分隔符的数字 K 1 , K 2 … K N
(−2 31 ≤ K i ≤ 2 31 − 1),代表 N 块区域的价值。每组数据占一行。
若 N 为 0,则表示输入结束,该组数据 不输出任何内容 。
输出
对于每组数据,在一行内输出一个正整数,表示游戏中可获得的最大价值。

题看着很长,其实意思就是有一条路,被分成若干区域,每块区域都有对应的价值,玩家站在起点处,他可以往前跳到任意区域,但是这个区域只能要么比他现在所在的区域价值大,要么直接跳到终点,跳到终点也意味着游戏结束。求最大价值
嗯...我只能说典型的动态规划题...别问我动态规划到底是个啥...我也不知道
看题吧:

  • 首先这个题不是看上去那么简单的,因为要考虑,在路上有个突然大的值,如果跳上去的话,后面很多值都跳不了了,如果后面本来可以跳的价值和比这一个突然大的值大,那么就不算最优解了,比如 1.2.10.3.4.5.6.7.8.9。
  • 我们可以参照第一题的思路,因为第一题是给出了层次,必须向上走,而这个是任意跳,所以我们要稍微改变一下
  • 从第一步开始,我们把每一步的最优解都算出来。先让每块区域的最优解就是它本身,从第一步开始不断优化,使每位的最优解变成真正的最优解,因为每一步需要有条件,(比当前区域大才可以走),所以不能直接在原数组上计算最优解,这样会影响后面的,所以需要另外开一个数组。
  • 找每位最优解的时候需要重头开始遍历,如果有区域的价值比当前区域小,那么比较当前区域的最优解和该区域的最优解加上当前区域价值哪个比较大,比较大的那个就是新的最优解,刷新最优解。dp[i]=max(dp[i],dp[j]+k[i])。
  • 还需要一个参数,在每次更新最优解后比较当前最大值和新的最优解哪个更大。
  • 最后最大值就是所求值。

代码:

#include<stdio.h>
int max(int a,int b)               //比较最大值
{
  if(a>b)
    return a;
  else
    return b;
}
int main()
{
  int i,j,n;
  while(1)
  {
    scanf("%d",&n);          //输入有多少个区域
    if(n==0)
    {
      break;
    }
    int k[n];
    int dp[n];
    int amax=-2147483648;
    for(i=0;i<n;i++)
    {
      scanf("%d",&k[i]);        //输入每个区域的价值
    }
    for(i=0;i<n;i++)           //遍历每个区域
    {
      dp[i]=k[i];
      for(j=0;j<i;j++)           //依次从头开始遍历,更新最优解
      {
        if(k[i]>k[j])
          dp[i]=max(dp[i],dp[j]+k[i]);
      }
      amax=max(amax,dp[i]);
    }
    printf("%d\n",amax);
  }
}

第四道: 超级跳跃 Again
玩过了很久的《超级跳跃》游戏之后,虽然游戏规则没有发生变化,但你的
心境发生了微妙的变化,你想经过更多的区域,看更多的风景。
输入
输入的数据有多组,每组数据首先是一个正整数 N (1 ≤ N ≤ 1000),代表
赛道被分为 N 块区域,接下来是 N 个以空格为分隔符的数字 K 1 , K 2 … K N ,
代表 N 块区域的价值。每组数据占一行。
输出
对于每组数据,在一行内输出一个正整数,表示最多可经过的区域数。

第四道跟第三道...没什么区别,我们把最优解里面的最大价值换成最长路径就可以,让每位的最优解都为1,然后在计算每一位的最优解时算的是满足条件下当前最优解和该区域最优解加1,dp[i]=max(dp[i],dp[j]+1)。
代码:

#include<stdio.h>
int max(int a,int b)
{
  if(a>b)
    return a;
  else
    return b;
}
int main()
{
  int i,j,n;
  while(scanf("%d",&n)!=EOF)
  {
    int k[n];
    int dp[n];
    int amax=1;
    for(i=0;i<n;i++)
    {
      scanf("%d",&k[i]);
    }
    for(i=0;i<n;i++)
    {
      dp[i]=1;
      for(j=0;j<i;j++)
      {
        if(k[i]>k[j])
          dp[i]=max(dp[i],dp[j]+1);
      }
      amax=max(amax,dp[i]);
    }
    printf("%d\n",amax);
  }
}

第五道:超级跳跃 Remake
最近,《超级跳跃》游戏进行了一次升级!近乎全新的《超级跳跃 Remake》
游戏已经上线啦,它的规则是这样的:
a. 游戏在一块 N × N 的区域中进行,每块区域具有一个价值 K (i, j) ;
b. 玩家起始站在(1, 1)区域处,并且获得这块区域的价值;
c. 玩家每次可以选择使用超级跳跃到达与当前位置距离为 M 的,并且方
向正好水平或者垂直的区域,且到达的区域的价值应当大于当前所在
的区域价值。当无法找到这样的区域时游戏结束。
d. 两块区域的距离是这样计算的:每向上下左右移动一次,距离加 1,
下图显示了黑色区域到可以到达的各个区域的距离:
这里写图片描述
e. 玩家每到达一个区域,就获得这块区域的价值。
请你编写一个程序,算出任意一个《超级跳跃 Remake》游戏的区域,玩家
最多可以获得多少价值。
输入
输入的数据有多组,每组数据首先是两个正整数 N 和 M (1 ≤ N, M ≤ 100)
表示在一个 N × N 的区域内进行游戏,且超级跳跃只能移动到距离为 M 的区
域中。接下来有 N 行,每行有 N 个以空格为分隔符的数字:
K (i,1) , K (i, 2) … K (i, N)
第 i 行中第 j 个数字代表(i, j)区域的价值。
若 N 和 M 均等于-1,表示输入结束,该组数据 不输出任何内容 。
输出
对于每组数据,在一行内输出一个正整数,表示游戏中可获得的最大价值。

思路都是一个思路,这道题就不仔细讲了,大家自己思考一下吧~
代码:

#include<stdio.h>
#include <stdlib.h>

struct cx
{
  int n;
  int x;
  int y;
};
int comp(const void*a,const void*b)
{
    return *(int*)a-*(int*)b;
}
int min(int a,int b)
{
  if(a>b)
    return b;
  else
    return a;
}
int max(int a,int b)
{
  if(a>b)
    return a;
  else
    return b;
}

int main()
{
  int i,j;
  while(1)
  {
    int n,m,amax,v,w;
    scanf("%d%d",&n,&m);
    if(n==-1 && m==-1)  //如果m,n都是-1,退出程序
    {
      break;
    }
    int k[n][n];
    int dp[n][n];
    struct cx c[n*n];
    v=0;
    for(i=0;i<n;i++)
    {
      for(j=0;j<n;j++)
      {
        scanf("%d",&k[i][j]);
        dp[i][j]=-1;
        c[v].n=k[i][j];
        c[v].x=i;
        c[v].y=j;
        v++;
      }
    }
    amax=k[0][0];
    dp[0][0]=k[0][0];
    qsort(c,n*n,sizeof(struct cx),comp);
    w=0;
    int x,y;
    while(v--)
    {
        x=c[w].x;
        y=c[w].y;
        for(i=max(0,x-m);i<=min(n-1,x+m);i++)
        {

          if(k[x][y]<k[i][y] && i!=x && dp[x][y]!=-1)
          {
            dp[i][y]=max(dp[i][y],k[i][y]+dp[x][y]);
            amax=max(dp[i][y],amax);
          }
        }
        for(i=max(0,y-m);i<=min(n-1,y+m);i++)
        {
          if(k[x][y]<k[x][i]  && i!=y && dp[x][y]!=-1)
          {
            dp[x][i]=max(dp[x][i],k[x][i]+dp[x][y]);
            amax=max(dp[x][i],amax);
          }
        }

      w++;
    }
    printf("%d\n",amax);
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值