分享对dp题目的理解-不断更新ing

5Y15H:

零钱兑换

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。

计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1

你可以认为每种硬币的数量是无限的。

依旧能用完全背包的模板,dfs代码如下:

cpp 复制代码
int dfs(vector<int>& coins,int x,int amount)
    {
        if(amount==0)return 0;
        if(x<0||amount<0)return 1e9;
        if(amount>=coins[x])
        return min(dfs(coins,x,amount-coins[x])+1,dfs(coins,x-1,amount));
        return dfs(coins,x-1,amount); 
    }

dfs(x,amount)=题目问题的答案,问的是什么,dfs返回的值就是什么。

对于每个硬币,有两种选择:

  1. 不拿,去下一个拿
  2. 拿了,继续拿。

把当前状态转移给下一步可能有的状态。

在之前的题目中,初始值都是0,因为题目问的是最大价值和,但我们这里问的是最小硬币数,为了否定某个方案,我们要返回1e9。

总结以下思路步骤:

1、dfs()=题目问题的答案。

2、想dfs的最后一步,也就是什么返回,返回什么。

3、当前状态怎么转移到其他状态或者说当前状态与下一个状态的关系。

4、考虑初始值。

dp代码:

cpp 复制代码
int coinChange(vector<int>& coins, int amount) {
    int n=coins.size();
    int arr[15][10010];
    memset(arr,0x3f,sizeof(arr));
    for(int i=0;i<n;i++)arr[i][0]=0;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<=amount;j++)//arr[1]==coins[0]的值
        { 
            if(j>=coins[i])arr[i+1][j]=min(arr[i+1][j-coins[i]]+1,arr[i][j]);
            else arr[i+1][j]=arr[i][j];
        }
    }
    int ans=arr[n][amount];
    return ans>=1e9?-1:ans;
    }

5Y15H

LCR 161. 连续天数的最高销售额

与它一模一样的题:

P1115 最大子段和

这题可以当做模板题

写法①:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int arr[N];
int dp[N];
int n;
int ans=-1e9;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)cin>>arr[i];
    for(int i=1;i<=n;i++)
    {
        //就是当前的值+之前的和与当前的值的较大值,记录最大的值
        dp[i]=max(dp[i-1]+arr[i],arr[i]);
        ans=max(ans,dp[i]);
    }
    cout<<ans;
    return 0;
}

写法②:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
//const int N=2e5+10;
//int arr[N];
//int dp[N];
int n;
int ans=-1e9;//比大小出结果时,ans要要考虑是-1e9;
/*
在一个数组中找某个连续的最大数组和,就是前面的价值+当前价值与当前价值做比较,b存当前的较大值
ans更新最大的b

*/
int a,b;
signed main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a;
        b+=a;
        ans=max(ans,b);//默认一开始是从第一个数开始的一段区间和,什么时候变起点呢sum<0
        if(b<0)b=0;
    }
    cout<<ans;
    return 0;
}

就记住 :就是当前的值+之前的和与当前的值的较大值,记录最大的值

要不就是第一次遇到b<0的情况,让b=0;


整数拆分

给定一个正整数 n ,将其拆分为 k正整数 的和( k >= 2 ),并使这些整数的乘积最大化。

返回 你可以获得的最大乘积

这题太有意思了。

💡想法①(纯dfs的思想):

从题目做的出来一些信息,k>=2&&k<=n;

可以枚举每次拿到的数字,并且用数组记录下来,代码边写边补充嘛

cpp 复制代码
dfs(int x)
{
for(int i=1;i<n;i++)
arr[x]=i;
}

3,3,4

3,4,3

4,3,3

这些都一样的,所以这还是个组合,要用start来规定那就是这样,再处理一下边界情况:

复制代码
void dfs(int start,int x,int sum,int n)
    {
        if(sum>n)return;
        if(sum==n)
        {
            int tmp=1;
            for(int i=1;i<x;i++)
            {
                tmp*=arr[i];
            }
            ans=max(ans,tmp);
        }
        if(x>n)return;
        for(int i=start;i<n;i++)
        {
            arr[x]=i;
            dfs(i,x+1,sum+i,n);
        }
}

💡想法②(完全背包的思想):

组合就是不能往拿过的位置拿,那完全背包符合这个性质,对于每个位置

  1. 拿了,继续拿
  2. 不拿,去下一个

不会往前拿

cpp 复制代码
dfs(int x)=max(dfs(x,sum-x)*x,dfs(x,sum));
dfs() :选择到第x个值时,和为sum,能得到的最大乘积

找到了状态转移公式后,再处理边界情况

cpp 复制代码
int dfs(int x,int sum)
    {
        if(sum==0)return 1;
        if(sum<0||x<1)return 0;
        if(sum>=x) return max(dfs(x,sum-x)*x,dfs(x-1,sum));
        return dfs(x-1,sum);
    }

最后,再写出递推来

cpp 复制代码
int integerBreak(int n) {
        int arr[n+1][n+1];
        memset(arr,0,sizeof(arr));
        arr[0][0]=1;
        for(int i=1;i<n;i++)
        {
            for(int j=0;j<=n;j++)
            {
                if(j>=i)arr[i][j]=max(arr[i][j-i]*i,arr[i-1][j]);
                else arr[i][j]=arr[i-1][j];
            }
        }
        return arr[n-1][n];
    }
相关推荐
有时间要学习1 小时前
【无标题】
算法
Chloeis Syntax1 小时前
JavaEE学习日记(2)---文件操作和IO
java·笔记·学习·java-ee
re林檎1 小时前
算法札记——5.15
算法
鱼子星_1 小时前
【数据结构与算法】OJ题目详解(一)-单链表:从易到难的面试OJ题目
c语言·数据结构·算法·链表·面试·职场和发展
人道领域1 小时前
【LeetCode刷题日记】递归与回溯实战 257.二叉树的所有路径——一篇文章彻底搞懂回溯
开发语言·python·算法·leetcode
ulias2121 小时前
leetcode热题 - 7
数据结构·算法·leetcode
吃好睡好便好1 小时前
在Matlab中用sphere( )函数绘制球面图
开发语言·前端·javascript·学习·算法·matlab·信息可视化
图码1 小时前
矩阵中的“对角线强迫症”:如何优雅地判断Toeplitz矩阵?
数据结构·c++·线性代数·算法·青少年编程·矩阵
lynnlovemin1 小时前
二分查找与二分答案算法详解(基于C++实现)
c语言·开发语言·算法·二分查找·二分答案