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返回的值就是什么。
对于每个硬币,有两种选择:
- 不拿,去下一个拿
- 拿了,继续拿。
把当前状态转移给下一步可能有的状态。
在之前的题目中,初始值都是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
与它一模一样的题:
这题可以当做模板题
写法①:
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);
}
}
💡想法②(完全背包的思想):
组合就是不能往拿过的位置拿,那完全背包符合这个性质,对于每个位置
- 拿了,继续拿
- 不拿,去下一个
不会往前拿
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];
}