[蓝桥杯学习]动态规划

题目描述
蒜头君很喜欢爬楼梯,但是蒜头君腿不够长,每次蒜头君最多只能一步跨越两个阶梯。比如他初始在楼底,跨越一个阶梯到达 1 号阶梯,或者跨越两个阶梯到达 2 号阶梯。如下图

为了选出一种最轻松的爬楼梯的方式,蒜头君想把所有不同的到达楼顶的方式都尝试一遍。对于一共有 n 个阶梯的楼梯,蒜头君一共有多少总方法从楼底到达楼顶。

由于最后答案可能很大,输出最后的答案对 100007 取模的结果。 

#include <iostream>
#include <queue>
using namespace std;
 
int main()
{
    int n,dp[1000];
    cin>>n;
    dp[0]=dp[1]=1;
    for(int i=2;i<=n;i++)
    {
        dp[i]=(dp[i-1]+dp[i-2])%100007;
    }
    cout>>dp[n]>>endl;

  return 0;
}

爬楼梯2 蒜头军可以跳跃任意奇数的阶梯

#include <iostream>
#include <queue>
using namespace std;
 
int main()
{
    int n,dp[1000];
    cin>>n;
    dp[0]=1;
    for(int i=2;i<=n;i++)
    {
        for(int j=i-1;j>0;j-=2)
        {
            dp[i]=dp[i]+dp[j];
            dp[i]%=10007;
        }
    }
    cout>>dp[n]>>endl;

  return 0;
}

问题
有一个小球掉落在一串连续的弹簧板上,小球落到某一个弹簧板后,会被弹到某一个地点,直到小球被弹到弹簧板以外的地方。

假设有 n 个连续的弹簧板,每个弹簧板占一个单位距离,a[i] 代表代表第 i 个弹簧板会把小球向前弹 a[i] 个距离。比如位置 1的弹簧能让小球前进 2 个距离到达位置 3。如果小球落到某个弹簧板后,经过一系列弹跳会被弹出弹簧板,那么小球就能从这个弹簧板弹出来。现在希望你计算出小球从任意一个弹簧板落下,最多会被弹多少次后,才会弹出弹簧板。

输入格式

第一个行输入一个 n 代表一共有 n 个弹簧板。第二行输入 n 个数字,中间用空格分开。第 i 个数字 a[i] 代表第 i 个弹簧板可以让小球移动的距离。

数据约定:

对于 50% 的数据:1 ≤ n ≤ 1000, 0 < a[i]≤30。

对于 100% 的数据:1≤n≤100000 , 0 < a[i]≤30。

输出格式

输出一个整数,代表小球最多经过多少次才能弹出弹簧板。 

#include <iostream>
#include<algorithm>
#include <cstdio>
#include<cstring>
using namespace std;

int main()
{
    int n,a[1000],dp[1000],ans=0;
    cin>>n;
    for(int i=0;i<n;i++)
    {
         cin>>a[i];
    }
    memset(dp,0,sizeof(dp));
    for(int j=n;j>0;j--)
    {
       /*dp[j+a[j]]=dp[j]+1;
        ans=max(ans,dp[j+a[j]]);*/
        dp[j]=dp[j+a[j]]+1;
        ans=max(dp[j],ans)
    }
    cout<<ans<<endl;

  return 0;
}

题目描述:
工作空闲之余,蒜头君经常带着同事们做游戏,最近蒜头君发明了一个好玩的新游戏:n 位同事围成一个圈,同事 A 手里拿着一个兔妮妮的娃娃。蒜头君喊游戏开始,每位手里拿着娃娃的同事可以选择将娃娃传给左边或者右边的同学,当蒜头君喊游戏结束时,停止传娃娃。此时手里拿着娃娃的同事即是败者。
玩了几轮之后,蒜头君想到一个问题:有多少种不同的方法,使得从同事 A 开始传娃娃,传了 m 次之后又回到了同事 A 手里。两种方法,如果接娃娃的同事不同,或者接娃娃的顺序不同均视为不同的方法。例如1−>2−>3−>1 和 1->3->2->1 是两种不同的方法。
输入格式
输入一行,输入两个整数n,m(3≤n≤30,1≤m≤30),表示一共有 n 位同事一起游戏,一共传 m 次娃娃。
输出格式
输出一行,输出一个整数,表示一共有多少种不同的传娃娃方法。
样例输入
3 3
样例输出
2 

#include <iostream>
#include<algorithm>
#include <cstdio>
#include<cstring>
using namespace std;

int main()
{
    int n,m,i,j;
    cin>>n>>m;
    int f[m+1][n+1];//考虑当前传到谁手里,传了多少次
    memset(f,0,sizeof(f));
    f[0][1]=1;//第一个人传走时传了0次,就一种方法
    for(i=1;i<=m;i++)
    {
        for(j=1;j<=n;j++)
        {
            if(j==1)
            {
                f[i][j]=f[i-1][n]+f[i-1][2];
            }
            else if(j==n)
            {
                f[i][j]=f[i-1][1]+f[i-1][n-1];
            }
            else
            {
                f[i][j]=f[i-1][j-1]+f[i-1][j+1];
            }
        }
    }
    cout<<f[m][1]<<endl;

  return 0;
} 

蒜头君在玩一款逃生的游戏。在一个n×m 的矩形地图上,蒜头位于其中一个点。地图上每个格子有加血的药剂,和掉血的火焰,药剂的药效不同,火焰的大小也不同,每个格子上有一个数字,如果格子上的数字是正数说明是一个药剂代表增加的生命值,如果是负数说明是火焰代表失去的生命值。

蒜头初始化有 v 点血量,他的血量上限是 c,任何时刻他的生命值都不能大于血量上限,如果血量为 0 则会死亡,不能继续游戏。

矩形地图上的四个角(1,1),(1,m),(n,1),(n,m)为游戏的出口。游戏中只要选定了一个出口,就必须朝着这个方向走。例如,选择了左下的出口,就只能往左和下两个方向前进,选择了右上的出口,就只能往右和上两个方向前进,左上和右下方向的出口同理。

如果成功逃生,那么剩余生命值越高,则游戏分数越高。为了能拿到最高分,请你帮忙计算如果成功逃生最多能剩余多少血量,如果不能逃生输出 -1。

输入格式
第一行依次输入整数 n,m,x,y,v,c(1<n,m≤1000,1≤x≤n,1≤y≤m,1≤v≤c≤10000), 其中 n,m 代表地图大小,(x,y) 代表蒜头君的初始位置,v 代表蒜头的初始化血量,c代表蒜头的生命值上限。

接下来 n 行,每行有 m 个数字,代表地图信息。(每个数字的绝对值不大于100,地图中蒜头君的初始位置的值一定为 0)

输入格式
第一行依次输入整数 n,m,x,y,v,c(1<n,m≤1000,1≤x≤n,1≤y≤m,1≤v≤c≤10000), 其中 n,m 代表地图大小,(x,y) 代表蒜头君的初始位置,v 代表蒜头的初始化血量,c代表蒜头的生命值上限。

接下来 n 行,每行有 m 个数字,代表地图信息。(每个数字的绝对值不大于100,地图中蒜头君的初始位置的值一定为 0)

输出格式
一行输出一个数字,代表成功逃生最多剩余的血量,如果失败输出 -1。

样例输入
4 4 3 2 5 10
1 2 3 4
-1 -2 -3 -4
4 0 2 1
-4 -3 -2 -1
样例输出
10

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<vector>
    #include<algorithm>
    #include<queue>
    #include<unordered_map>
    using namespace std;
 
    int n, m, x, y, v, c;
    int mp[1005][1005];  //地图
    int dp[1005][1005] ;  //每个点的状态
    int xx[4] = { -1,-1,1,1 };  //分别表示左上、右上、右下、左下方向
    int yy[4] = { -1,1,1,-1 };
    const int INF = 0x3f3f;
 
    int main() {
        //初始化
        cin >> n >> m >> x >> y >> v >> c;
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j<= m; ++j) {
                cin >> mp[i][j];
            }
        }
        //分别往四个出口走,例如往(1,1)走,前进方向只能是向左或向上,而对于此时dp[i][j]来讲,
        //在不考虑边界的情况他的来源只能是他的正下方dp[i+1][j]或正右方dp[i][j+1],取max即可
        for (int t = 0; t < 4; ++t) {
            for (int i = x; i > 0 && i <= n; i += xx[t])
                for (int j = y; j > 0 && j <= m; j += yy[t]) {
                    if (i == x && j == y) { //起点
                        dp[i][j] = v;
                    }
                    else if (i == x) {  //如果跟起点同一行,则只能为列变化
                        dp[i][j] = min(c, dp[i][j - yy[t]] + mp[i][j]);   //注意这里的min和下面的max代表的意义
                    }
                    else if (j == y) {  //如果跟起点同一列,则只能为行变化
                        dp[i][j] = min(c, dp[i-xx[t]][j] + mp[i][j]);
                    }
                    else {
                        dp[i][j] = min(c, max(dp[i - xx[t]][j], dp[i][j - yy[t]])+mp[i][j]);
                    }
                    if (dp[i][j] <= 0) {  //已经死掉
                        dp[i][j] = -INF;
                    }
                }
        }
        int ans = max(max(dp[1][1], dp[1][m]), max(dp[n][1], dp[n][m]));
        if (ans <= 0) {
            cout << -1 << endl;
            return 0;
        }
        else {
            cout << ans << endl;
        }
  
        return 0;
    }

在一个夜黑风高的晚上,有n(n <= 50)个小朋友在桥的这边,现在他们需要过桥,但是由于桥很窄,每次只允许不大于两人通过,他们只有一个手电筒,所以每次过桥的两个人需要把手电筒带回来,i号小朋友过桥的时间为a[i],两个人过桥的总时间为二者中时间长者。问所有小朋友过桥的总时间最短是多少。 

分析

1、将a[n]从小到大进行排序;

2、考虑第i个人在桥的这边,

假设前i-1个人都在桥对面有dp[i]=dp[i-1]+a[1]+a[i](让第一个人回来送电筒,然后第i个人和第一个人过去,手电筒保持在桥对面),

假设前i-2个人都在桥对面有dp[i]=dp[i-2]+a[0]+a[i]+2*a[2](让第一个人回来送电筒,然后第i-1个人和第i个人过去,之后花费时间次少的第二个人过来将第一个人接过去)

#include <iostream>
#include<algorithm>
#include <cstdio>
#include<cstring>
using namespace std;

int main()
{
     int n;
     cin>>n;
     int a[n+1],dp[n+1];
     for(int i=0;i<n;i++)
        cin>>a[i];
     sort(a,a+n);
     dp[0]=a[0];
     dp[1]=a[1];
     for(int i=2;i<n;i++)
     {
         dp[i]=min(dp[i-1]+a[0]+a[i],dp[i-2]+a[0]+a[i]+2*a[1]);
     }
     cout<<dp[n-1]<<endl;
     return 0;
}

最大子矩阵和

#include <iostream>
#include <math.h>
#include <cstring>
#include <algorithm>
using namespace std;
/*最大子矩阵和*/
int main()
{
  long long perfix_sum[101][101],a[101][101];
  int n,m,i,j,k;
  long long ans=0,sum;
  cin>>n>>m;
  for(i=1;i<=n;i++)
  {
      for(j=1;j<=m;j++)
      {
          cin>>a[i][j];
          ans=max(a[i][j],ans);
      }
  }
  for(i=1;i<=n;i++)
  {
      for(j=1;j<=m;j++)
      {
          perfix_sum[i][j]+=perfix_sum[i-1][j]+a[i][j];
      }
  }
  for(i=1;i<=n;i++)
  {
      for(j=i;j<=n;j++)
      {
          sum=0;
          for(k=1;k<m;k++)
          {
              if(sum+perfix_sum[j][k]-perfix_sum[i-1][k]<0)
                sum=0;
              else
                sum+=perfix_sum[j][k]-perfix_sum[i-1][k];
                ans=max(sum,ans);
          }
      }
  }
  cout<<ans<<endl;
  return 0;
}

问题描述

给定有 n 个数的 A 序列:A1,A2,A3…An 。对于这个序列,我们想得到一个子序列 Ap1,Ap2⋯Api⋯Apm(1≤p1< p2<⋯pi<⋯< pm≤n),
满足 Ap1≥Ap2≥⋯≥Api≤⋯≤Apm 。从 A 序列最少删除多少元素,可以得到我们想要的子序列。
输入格式
第一行输入一个整数 n,代表 A 序列中数字的个数。第二个输入 n 个整数,代表A1,A2 ,A3 …An。(1≤n≤1000,1≤Ai≤10000)
输出格式
输出需要删除的元素个数,占一行。
样例输入
7
3 2 4 1 2 5 3
样例输出
2

#include <iostream>
#include <math.h>
#include <cstring>
#include <algorithm>
using namespace std;
int main()
{
   int n,i,j,a[1000];
   cin>>n;
   int** dp=new int*[n];
   for(i=0;i<n;i++)
      dp[i]=new int[n];
   for(i=0;i<n;i++)
    cin>>a[i];
    for(i=0;i<n;i++)
    {
        for(j=0;j<n;j++)
            dp[i][j]=0;
    }
   for(i=0;i<n;i++)
   {
       dp[0][i]=1;
       for(j=0;j<i;j++)
       {
           if(a[j]>=a[i])
           {
               dp[0][i]=max(dp[0][i],dp[0][j]+1);
           }
       }
   }
   for(i=n-1;i>=0;i--)
   {
       dp[1][i]=1;
       for(j=n-1;j>i;j--)
       {
           if(a[j]>=a[i])
           {
               dp[1][i]=max(dp[1][i],dp[1][j]+1);
           }
       }
   }
   int ans=0;
   for(i=0;i<n;i++)
        ans=max(ans,dp[0][i]+dp[1][i]-1);
   cout<<n-ans<<endl;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值