算法笔记(动态规划入门题)

1.找零钱

int coinChange(int* coins, int coinsSize, int amount) {
    int dp[amount + 1];
    memset(dp,-1,sizeof(dp));
    dp[0] = 0;
    for (int i = 1; i <= amount; i++)
        for (int j = 0; j < coinsSize; j++)
            if (coins[j] <= i && dp[i - coins[j]] != -1)
                if (dp[i] == -1 || dp[i] > dp[i - coins[j]] + 1)
                    dp[i] = dp[i - coins[j]] + 1;
    return dp[amount];
}

2.不同路径

int uniquePaths(int m, int n) {
    int dp[m+1][n+1];
    memset(dp,0,sizeof(dp));
    for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++){
            if(i==1||j==1)
                dp[i][j]=1;
            else
                dp[i][j]=dp[i-1][j]+dp[i][j-1];
        }
    return dp[m][n];
}

3.最小路径和

int minPathSum(int** grid, int gridSize, int* gridColSize) {
    int m=gridSize,n=gridColSize[0];
    int dp[m][n];
    memset(dp,0,sizeof(dp));
    dp[0][0]=grid[0][0];
    for(int i=1;i<m;i++)
        dp[i][0]=dp[i-1][0]+grid[i][0];
    for(int j=1;j<n;j++)
        dp[0][j]=dp[0][j-1]+grid[0][j];
    for(int i=1;i<m;i++)
        for(int j=1;j<n;j++)
            dp[i][j]=fmin(dp[i-1][j],dp[i][j-1])+grid[i][j];
    return dp[m-1][n-1];
}

4.有奖问答

#include <iostream>
using namespace std;
int ans=0;
int dp[31][10];//dp[i][j]代表回答了i道题目时得到了j*10的分数的 总方案数
int main(){
  dp[0][0] = 1;//初始化起点,起点就表示一个方案数
  for(int i = 1;i<=30;i++)
    for(int j = 0;j<=9;j++)
      if(j==0)//得到零分,说明这一题答错了,那么方案数量就是上一题的所有方案之和,上一题多少分都不影响当前题,因为一旦答错,分数归零。
        for(int k = 0;k<=9;k++)
          dp[i][j] += dp[i-1][k];
      else//答对了,那么说明这个方案必须承接上一次答对的方案数,上一题必须是当前分数-10,即j-1道题。
        dp[i][j] = dp[i-1][j-1];
  for(int i = 0;i<=30;i++)
    ans+=dp[i][7];//记录所有答对7次的方案数
  cout<<ans;
  return 0;answerquest
}

5.字符串转换

#include <iostream>
#include <cstdio>
#include <string>
using namespace std;
string s,t;
int transform(){
  int l1=s.length(),l2=t.length();
  int dp[l1+1][l2+1];
  for(int i=0;i<l1;i++)
    dp[i][0]=i;
  for(int j=0;j<l2;j++)
    dp[0][j]=j;
  for(int i=1;i<=l1;i++)
    for(int j=1;j<=l2;j++){
      if(s[i-1]==t[j-1])
        dp[i][j]=dp[i-1][j-1];
      else
        dp[i][j]=min(min(dp[i][j-1],dp[i-1][j]),dp[i-1][j-1])+1;
    }
  return dp[l1][l2];
}
int main()
{
  // 请在此输入您的代码
  cin>>s>>t;
  printf("%d",transform());
  return 0;
}

动态规划浅析——记一道困难的字符串操作数问题 - 知乎 (zhihu.com),这个文章写的很不错,可以看看。

6.完全背包问题

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int n,v;
struct obj{
  int v;//体积
  int c;//价值
};
int packet(obj o[]){
  int dp[n+1][v+1];//选第i个物品且体积为j时的价值
  memset(dp,0,sizeof(dp));
  for(int i=1;i<=n;i++){
    for(int j=0;j<=v;j++){
      dp[i][j]=dp[i-1][j];
      for(int k=0;k*o[i].v<=j;k++){
        dp[i][j]=max(dp[i][j],dp[i-1][j-k*o[i].v]+k*o[i].c);
      }
    }
  }
  return dp[n][v];
}
int main()
{
  // 请在此输入您的代码
  scanf("%d%d",&n,&v);
  obj o[n+1];
  o[0].v=0,o[0].c=0;
  for(int i=1;i<=n;i++)
    scanf("%d%d",&o[i].v,&o[i].c);
  printf("%d",packet(o));
  return 0;
}

7.松散子序列

#include <iostream>
#include <string>
#include <cstring>
using namespace std;
string s;
inline int value(char a){
  return a-'a'+1;
}
int SubSeq(){
  int len=s.length();
  int dp[len];
  memset(dp,0,sizeof(dp));
  dp[0]=value(s[0]);
  dp[1]=max(dp[0],value(s[1]));
  for(int i=2;i<len;i++)
    dp[i]=max(dp[i-1],dp[i-2]+value(s[i]));
  return dp[len-1];
}
int main()
{
  // 请在此输入您的代码
  cin>>s;
  cout<<SubSeq();
  return 0;
}
//字符串版的打家劫舍,挺简单的

8.游戏中的学问

#include <iostream>
#include <cstring>
using namespace std;
long long p;
int n,k;
long long circle(){
  long long dp[n+1][k+1];
  memset(dp,0,sizeof(dp));
  dp[3][1]=2;
  for(int i=4;i<=n;i++)
    for(int j=1;j<=k;j++){
      dp[i][j]=(dp[i-1][j]*(i-1))%p;//加入已有的圈
      dp[i][j]=(dp[i][j]+dp[i-3][j-1]*(i-2)*(i-1))%p;
      //组成新的圈,i已确定,第二个人有i-1个选择,第三个人有i-2个选择
    }。
  return dp[n][k]%p;
}
int main()
{
  // 请在此输入您的代码
  scanf("%d%d%lld",&n,&k,&p);
  printf("%lld",circle());
  return 0;
}
//高中的排列组合题,还是有点难度的

9.蜗牛

#include<iostream>
#include<cstdio>
using namespace std;
const int N = 1e5 + 10;
int a[N],b[N],x[N];//a[i]表示窝牛在第i个轴的高度; 
double f[N][2];//f[N][0]表示,到达第n个点的最小时间;f[N][1] 表示到达第n个点的a[n]这个高度的最短时间; 
int main() {
    int n;
    scanf("%d",&n);
    for(int i = 1; i <= n;i++)
        scanf("%d",&x[i]);
    for(int i = 1; i < n;i++)
        scanf("%d %d",&a[i],&b[i+1]);
    f[1][0] = x[1]; 
    f[1][1] =  x[1] + a[1] / 0.7;
    for(int i = 2;i <= n;i++) {
        f[i][0] = min(f[i-1][0] + (x[i] - x[i-1]),f[i-1][1]+ b[i] / 1.3);
        if(a[i] >= b[i]) 
          f[i][1] = min(f[i][0]+a[i]/0.7,f[i-1][1]+(a[i]-b[i])/0.7);
        else
          f[i][1] = min(f[i][0]+a[i]/0.7,f[i-1][1]+(b[i]-a[i])/1.3);
    }
    printf("%.2lf",f[n][0]); 
    return 0;
}//感觉最难的是构造状态转移方程,求解过程比较简单

10.数组分割

#include <iostream>
#include <cstring>
using namespace std;
#define N 1001
#define P 1000000007
int t,n;
long long a[N];
long long SubArr(){
  long long dp[n+1][2];
  memset(dp,0,sizeof(dp));
  dp[0][0]=1;
  for(int i=0;i<n;i++){
    if(a[i]%2==0){
      dp[i+1][0]=dp[i][0]*2;
      dp[i+1][1]=dp[i][1]*2;
    }
    else{
      dp[i+1][0]=(dp[i][0]+dp[i][1]);
      dp[i+1][1]=(dp[i][1]+dp[i][0]);
    }
    dp[i+1][0]%=P;
    dp[i+1][1]%=P;
  }
  return dp[n][0];
}
int main()
{
  // 请在此输入您的代码
  scanf("%d",&t);
  while(t--){
    long long sum=0;
    scanf("%d",&n);
    for(int i=0;i<n;i++){
      scanf("%lld",&a[i]);
      sum+=a[i];
    }
    if(sum%2==0)
      printf("%lld\n",SubArr());
    else
      printf("0\n");
  }
  return 0;
}//主要考察奇偶数相加性质:
   奇+偶=奇
   偶+偶=偶
   奇+奇=偶
感觉构造状态转移方程挺难的

蓝桥杯十四届JavaB组试题C:数组分割详解(dp)-CSDN博客,我是看这篇文章的解析。

11.接龙数组

#include <iostream>
#include <string>
#include <cstring>
using namespace std;
int n;
int ChainArr(string s[]){
  int x,y,m=0,dp[10];
  memset(dp,0,sizeof(dp));
  for(int i=0;i<n;i++){
    x=s[i][0]-'0';
    y=s[i][s[i].size()-1]-'0';
    dp[y]=max(dp[x]+1,dp[y]);
    m=max(m,dp[y]);
  }
  return n-m;
}
int main()
{
  // 请在此输入您的代码
  scanf("%d",&n);
  string s[n];
  for(int i=0;i<n;i++)
    cin>>s[i];
  if(n==1)
    printf("0");
  else
    printf("%d",ChainArr(s));
  return 0;
}//挺简单的题,但是状态转移方程构造很巧妙

这个题解写的很清晰 

————部分代码是别人写的题解,本人仅为转载,非原创;

  • 10
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
动态规划是一种常用的算法思想,用于解决具有重叠子问和最优子结构性质的问。下面是动态规划算法入门到精通的介绍: 入门: 1. 理解问的最优子结构性质:动态规划算法通常通过将原问分解为子问来求解,而子问的最优解可以用来构建原问的最优解。 2. 定义状态:将原问划分为若干个子问,并定义状态表示子问的解。 3. 确定状态转移方程:根据子问之间的关系,确定状态转移方程,即如何通过已知的子问的解来求解当前问的解。 4. 确定边界条件:确定最简单的子问的解,即边界条件。 进阶: 1. 优化状态转移方程:通过观察和分析,可以对状态转移方程进行优化,减少计算量或空间复杂度。 2. 记忆化搜索:使用数组或哈希表等数据结构来保存已经计算过的子问的解,避免重复计算。 3. 自底向上的迭代方法:从最简单的子问开始,逐步迭代求解更复杂的问,直到求解原问。 精通: 1. 状态压缩技巧:对于某些问,可以使用位运算等技巧将状态表示压缩为一个整数,从而减少空间复杂度。 2. 优化空间复杂度:对于某些问,可以只保存必要的中间结果,而不是保存所有子问的解,从而减少空间复杂度。 3. 优化时间复杂度:通过分析问的特点,可以对状态转移方程进行优化,减少计算量或提前终止计算。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值