1.背包dp基本方法
背包问题的各种类型,基本上是给一堆物品和体积、个数的限制,要求价值最大,有个别基于同样背景下却求数值的题
分类:
01背包:每件物品放或不放
多重背包:每件物品有限个
完全背包:每件物品无限个
分组背包:一组中只能选一件物品
纬度:
最好理解的是f[i][j],但视情况可以用滚动数组优化到一纬
完全背包题目
322. 零钱兑换
class Solution {
public:
int coinChange(vector<int>& coins, int m) {
int n = coins.size();
int init = m + 1;//初始化成一个大的数
vector<int> f(m + 1, init);
f[0] = 0;
for(int i = 1; i <= n; i ++)
for(int j = coins[i - 1]; j <= m; j ++)
f[j] = min(f[j], f[j - coins[i - 1]] + 1);//min的完全背包
return f[m] != init ? f[m] : -1;//判断是否能凑够
}
};
518. 零钱兑换 II
class Solution {
public:
/*
1.状态表示:f[n][m],硬币种类[0,n-1],对应[1,n],金额总数[1,m]对应[1,m]
2.状态属性:数值
3.状态转移:f[i][j]代表从前i种硬币中选,金额为j的方法数,转移为f[i][j] = f[i - 1][j]+(满足条件)f[i][j - coins[i - 1]]
*/
int change(int m, vector<int>& coins) {
int n = coins.size();
vector<int> f(m + 1, 0);
f[0] = 1;//初始化
for(int i = 1; i <= n; i ++)
for(int j = coins[i - 1]; j <= m; j ++)
f[j] += f[j - coins[i - 1]];
return f[m];
}
};
剑指 Offer 60. n个骰子的点数
class Solution {
public:
/*
1.状态表示:f[n][m],投的硬币数[0,n-1],对应[1,n],点数总数[1,m]对应[1,m]
2.状态属性:数值
3.状态转移:f[i][j]代表从前i个骰子中选,点数和为j的方法数。枚举第i个骰子投出的点数1-min(j,6),则f[i][j] += f[i-1][j-k]
*/
vector<double> twoSum(int n) {
vector<vector<int>> f(n + 1,vector<int>(n * 6 + 1, 0));
f[0][0] = 1;
for(int i = 1; i <= n; i ++)
for(int j = 1;j <= i * 6; j ++)
{
for(int k = 1;k <= min(6,j);k ++)//用的骰子点数最多为:min(j,6)
f[i][j] += f[i-1][j-k];
}
vector<double> res;
for(int i =n; i <= 6 * n; i ++) res.push_back(f[n][i]);
int s = 0;
for(int i = 0;i < res.size();i ++) s += res[i];
for(int i = 0;i < res.size(); i ++) res[i] /= s;
return res;
}
};
279. 完全平方数
class Solution {
public:
/*
完全背包问题
*/
class Solution {
public:
/*
完全背包问题
*/
int numSquares(int n) {
vector<int> choices;
int i = 1;
while(i <= n / i)//i*i <=n
{
choices.push_back(i * i);
i ++;
}
int m = choices.size();//物品个数
int init = n + 1;
vector<int> f(n + 1, init);
f[0] = 0;
for(int i = 1; i <= m; i ++)
for(int j = choices[i - 1]; j <=n; j ++)
f[j] = min(f[j], f[j - choices[i - 1]] + 1);
return f[n];
}
};
部分内容来自y总的教学,致谢!