leetcode-动态规划20
leetcode-1049-最后一块石头的重量II
本题其实是尽量让石头分成重量相同的两堆(尽可能相同),相撞之后剩下的石头就是最小的。一堆的石头重量是sum,那么我们就尽可能拼成 重量为 sum / 2 的石头堆。 这样剩下的石头堆也是 尽可能接近 sum/2 的重量。 那么此时问题就是有一堆石头,每个石头都有自己的重量,是否可以 装满 最大重量为 sum / 2的背包。
dp[j]表示容量(这里说容量更形象,其实就是重量)为j的背包,最多可以背最大重量为dp[j]。
int lastStoneWeightII(int* stones, int stonesSize) {
int sum = 0;
for(int i = 0 ; i < stonesSize ; i++){
sum += stones[i];
}
int target = sum / 2;
int i, j;
int* dp = (int*)malloc(sizeof(int)*(target+1));
memset(dp,0,sizeof(int)*(target+1));
for(int j = stones[0] ; j <= target ; j++){
dp[j] = stones[0];
}
for(int i = 1 ; i < stonesSize ; i++){
for(int j = target ; j >= stones[i] ; j--){
dp[j] = fmax(dp[j],dp[j-stones[i]]+stones[i]);
}
}
return (sum - dp[target]) - dp[target];
}
leetcode-494-目标和
1.纯0-1背包 是求给定背包容量的最大价值
2.分割等和子集 是求给定背包容量,能不能装满这个背包
3.最后一块石头的重量II 是求给定背包容量,尽可能装,能装多少装多少
4.目标和 是求给定背包容量,装满背包有多少种方法
5.一和零 是求给定背包容量,装满背包最多有多少个物品。
1.二维dp
被减数设为left 减数设为right
left + right = sum left - right = target ==> left = (sum+target)/2
dp[i][j] 使用 下标为[0, i]的nums[i]能够凑满j(包括j)这么大容量的包,有dp[i][j]种方法。
(1).若(sum+target)%2 不等于0 直接返回0
(2).若abs(target) > sum 直接返回0
(3)left即为背包最大容量
(4)第一列的状态需要由此前出现0的个数确定
(5)dp[0][nums[0]] = 1
当nums[i] > j时,这时候nums[i]一定不能取,所以是dp[i-1][j]种方案数
当nums[i] <= j,nums[i]可取可不取 dp[i][j] = dp[i-1][j] + dp[i-1]j-nums[i]]
int findTargetSumWays(int* nums, int numsSize, int target) {
int sum = 0;
for(int i = 0 ; i < numsSize ; i++){
sum += nums[i];
}
if((sum+target)%2 == 1)
return 0;
if(abs(target) > sum)
return 0;
int zeroNum = 0;
int left = (sum + target)/2;
int** dp = (int**)malloc(sizeof(int*)*numsSize);
for(int i = 0 ; i < numsSize ; i++){
dp[i] = (int*)malloc(sizeof(int)*(left+1));
for(int j = 0 ; j<= left ; j++){
dp[i][j] = 0;
}
}
if (nums[0] <= left) dp[0][nums[0]] = 1;
for (int i = 0; i < numsSize; i++) {
if (nums[i] == 0)
zeroNum++;
dp[i][0] = (int)pow(2, zeroNum);
}
for (int i = 1; i < numsSize; i++) {
for (int j = 1; j <= left; j++) {
if (j >= nums[i]) {
dp[i][j] = dp[i - 1][j] + dp[i - 1][j - nums[i]];
}
else {
dp[i][j] = dp[i - 1][j];
}
}
}
return dp[numsSize-1][left];
}
2.一维dp
dp[j]表示:填满j(包括j)这么大容积的包,有dp[j]种方法。
dp[j] = dp[j] + dp[j - nums[i]]
遍历物品放在外循环,遍历背包在内循环,且内循环倒序(为了保证物品只使用一次)
注:dp[0] = 1
int findTargetSumWays(int* nums, int numsSize, int target) {
int sum = 0;
for(int i = 0 ; i < numsSize ; i++){
sum += nums[i];
}
if((sum+target)%2 == 1)
return 0;
if(abs(target) > sum)
return 0;
int left = (sum + target)/2;
int* dp = (int*)malloc(sizeof(int)*(left+1));
for(int i = 0 ; i <= left ; i++){
dp[i] = 0;
}
dp[0] = 1;
for(int i = 0 ; i < numsSize ; i++){
for(int j = left ; j >= nums[i] ; j--){
dp[j] += dp[j-nums[i]];
}
}
return dp[left];
}
leetcode-474-一和零
int findMaxForm(char** strs, int strsSize, int m, int n) {
int oneNum = 0;
int zeroNum = 0;
int** dp = (int**)malloc(sizeof(int*)*(m+1));
for(int i = 0 ; i <= m ; i++){
dp[i] = (int*)malloc(sizeof(int)*(n+1));
for(int j = 0 ; j <= n ; j++){
dp[i][j] = 0;
}
}
for(int i = 0 ; i < strsSize ; i++){
int len = strlen(strs[i]);
for(int j = 0 ; j < len ; j++){
if(strs[i][j] == '0')
zeroNum++;
if(strs[i][j] == '1')
oneNum++;
}
for (int i = m; i >= zeroNum; i--) {
for (int j = n; j >= oneNum; j--) {
dp[i][j] = fmax(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
}
}
}
return dp[m][n];
}
kamaCoder-52-携带研究材料
完全背包物品可以无限次选 dp[i][j] = fmax(dp[i-1][j],dp[i][j-weight[i]]+value[i]);
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main(){
int n, bagSize;
scanf("%d %d",&n,&bagSize);
int* weight = (int*)malloc(sizeof(int)*n);
int* value = (int*)malloc(sizeof(int)*n);
for(int i = 0 ; i < n ; i++){
scanf("%d",&weight[i]);
scanf("%d",&value[i]);
}
int** dp = (int**)malloc(sizeof(int*)*n);
for(int i = 0 ; i < n; i++){
dp[i] = (int*)malloc(sizeof(int)*(bagSize+1));
for(int j = 0 ; j <= bagSize ;j++){
dp[i][j] = 0;
}
}
for(int j = weight[0] ; j <= bagSize ; j++){
dp[0][j] = dp[0][j-weight[0]] + value[0];
}
for(int i = 1 ; i < n ; i++){
for(int j = 1 ; j <= bagSize ; j++){
if(j >= weight[i]){
dp[i][j] = fmax(dp[i-1][j],dp[i][j-weight[i]]+value[i]);
}else{
dp[i][j] = dp[i-1][j];
}
}
}
printf("%d",dp[n-1][bagSize]);
return 0;
}
leetcode-518-零钱兑换II
完全背包,物品可以无限选 dp[i][j] = dp[i-1][j] + dp[i][j-coins[i]];
unsigned long long
int change(int amount, int* coins, int coinsSize) {
unsigned long long** dp = (unsigned long long**)malloc(sizeof(unsigned long long*)*coinsSize);
for(int i = 0 ; i < coinsSize ; i++){
dp[i] = (unsigned long long*)malloc(sizeof(unsigned long long)*(amount+1));
for(int j = 0 ; j <= amount ; j++){
dp[i][j] = 0;
}
}
for(int j = 0 ; j <= amount ; j++){
if(j % coins[0] == 0) dp[0][j] = 1;
}
for(int i = 0 ; i < coinsSize ; i++){
dp[i][0] = 1;
}
for(int i = 1 ; i < coinsSize ; i++){
for(int j = 0 ; j <= amount ; j++){
if(j >= coins[i]){
dp[i][j] = dp[i-1][j] + dp[i][j-coins[i]];
}else{
dp[i][j] = dp[i-1][j];
}
}
}
return dp[coinsSize-1][amount];
}
一维dp 此时内层循环正序遍历,物品可以重复选
int change(int amount, int* coins, int coinsSize) {
unsigned long long* dp = (unsigned long long*)malloc(sizeof(unsigned long long*)*(amount+1));
for(int i = 0 ; i <= amount ; i++){
dp[i] = 0;
}
dp[0] = 1;
for(int i = 0 ; i < coinsSize ; i++){
for(int j = coins[i] ; j <= amount ; j++){
dp[j] += dp[j-coins[i]];
}
}
return dp[amount];
}