Given weights and values of N items and the capacity W of the knapsack. Also given that the weight of at most K items can be changed to half of its original weight. The task is to find the maximum sum of values of N items that can be obtained such that the sum of weights of items in knapsack does not exceed the given capacity W.
Examples:
Input: W = 4, K = 1, value = [17, 20, 10, 15], weight = [4, 2, 7, 5]
Output: 37
Explanation: Change the weight of at most K items to half of the weight in a optimal way to get maximum value. Decrease the weight of first item to half and add second item weight the resultant sum of value is 37 which is maximum
Input: W = 8, K = 2, value = [17, 20, 10, 15], weight = [4, 2, 7, 5]
Output: 52
Explanation: Change the weight of the last item and first item and the add the weight the of the 2nd item, The total sum value of item will be 52.
Approach: Given problem is the variation of the 0 1 knapsack problem. Flag indicates number of items whose weight has been reduced to half. At every recursive call maximum of following cases is calculated and returned:
- Base case: If the index exceeds the length of values then return zero
- If flag is equal to K, maximum of 2 cases is considered:
- Include item with full weight if item's weight does not exceed remaining weight
- Skip the item
- If flag is less than K, maximum of 3 cases is considered:
- Include item with full weight if item's weight does not exceed remaining weight
- Include item with half weight if item's half weight does not exceed remaining weight
- Skip the item
// C++ Program to implement
// the above approach
#include <bits/stdc++.h>
using namespace std;
// Function to find the maximum value
int maximum(int value[], int weight[], int weight1,
int flag, int K, int index, int val_len)
{
// base condition
if (index >= val_len) {
return 0;
}
// K elements already reduced
// to half of their weight
if (flag == K) {
// Dont include item
int skip = maximum(value, weight, weight1, flag, K,
index + 1, val_len);
int full = 0;
// If weight of the item is
// less than or equal to the
// remaining weight then include
// the item
if (weight[index] <= weight1) {
full = value[index]
+ maximum(value, weight,
weight1 - weight[index], flag,
K, index + 1, val_len);
}
// Return the maximum of
// both cases
return max(full, skip);
}
// If the weight reduction to half
// is possible
else {
// Skip the item
int skip = maximum(value, weight, weight1, flag, K,
index + 1, val_len);
int full = 0;
int half = 0;
// Include item with full weight
// if weight of the item is less
// than the remaining weight
if (weight[index] <= weight1) {
full = value[index]
+ maximum(value, weight,
weight1 - weight[index], flag,
K, index + 1, val_len);
}
// Include item with half weight
// if half weight of the item is
// less than the remaining weight
if (weight[index] / 2 <= weight1) {
half = value[index]
+ maximum(value, weight,
weight1 - weight[index] / 2,
flag + 1, K, index + 1,
val_len);
}
// Return the maximum of all 3 cases
return max(full, max(skip, half));
}
}
int main()
{
int value[] = { 17, 20, 10, 15 };
int weight[] = { 4, 2, 7, 5 };
int K = 1;
int W = 4;
int val_len = sizeof(value) / sizeof(value[0]);
cout << (maximum(value, weight, W, 0, K, 0, val_len));
return 0;
}
// This code is contributed by Potta Lokesh
// Java implementation for the above approach
import java.io.*;
import java.util.*;
class GFG {
// Function to find the maximum value
static int maximum(int value[], int weight[],
int weight1, int flag, int K,
int index)
{
// base condition
if (index >= value.length) {
return 0;
}
// K elements already reduced
// to half of their weight
if (flag == K) {
// Dont include item
int skip = maximum(value, weight, weight1, flag,
K, index + 1);
int full = 0;
// If weight of the item is
// less than or equal to the
// remaining weight then include
// the item
if (weight[index] <= weight1) {
full = value[index]
+ maximum(value, weight,
weight1 - weight[index],
flag, K, index + 1);
}
// Return the maximum of
// both cases
return Math.max(full, skip);
}
// If the weight reduction to half
// is possible
else {
// Skip the item
int skip = maximum(value, weight, weight1, flag,
K, index + 1);
int full = 0;
int half = 0;
// Include item with full weight
// if weight of the item is less
// than the remaining weight
if (weight[index] <= weight1) {
full = value[index]
+ maximum(value, weight,
weight1 - weight[index],
flag, K, index + 1);
}
// Include item with half weight
// if half weight of the item is
// less than the remaining weight
if (weight[index] / 2 <= weight1) {
half
= value[index]
+ maximum(value, weight,
weight1 - weight[index] / 2,
flag, K, index + 1);
}
// Return the maximum of all 3 cases
return Math.max(full, Math.max(skip, half));
}
}
public static void main(String[] args) throws Exception
{
int value[] = { 17, 20, 10, 15 };
int weight[] = { 4, 2, 7, 5 };
int K = 1;
int W = 4;
System.out.println(
maximum(value, weight, W, 0, K, 0));
}
}
# Python program for the above approach
# Function to find the maximum value
def maximum(value,
weight, weight1,
flag, K, index, val_len):
# base condition
if (index >= val_len):
return 0
# K elements already reduced
# to half of their weight
if (flag == K):
# Dont include item
skip = maximum(value,
weight, weight1,
flag, K, index + 1, val_len)
full = 0
# If weight of the item is
# less than or equal to the
# remaining weight then include
# the item
if (weight[index] <= weight1):
full = value[index] + maximum(
value, weight,
weight1 - weight[index], flag,
K, index + 1, val_len)
# Return the maximum of
# both cases
return max(full, skip)
# If the weight reduction to half
# is possible
else:
# Skip the item
skip = maximum(
value, weight,
weight1, flag,
K, index + 1, val_len)
full = 0
half = 0
# Include item with full weight
# if weight of the item is less
# than the remaining weight
if (weight[index] <= weight1):
full = value[index] + maximum(
value, weight,
weight1 - weight[index],
flag, K, index + 1, val_len)
# Include item with half weight
# if half weight of the item is
# less than the remaining weight
if (weight[index] / 2 <= weight1):
half = value[index] + maximum(
value, weight,
weight1 - weight[index] / 2,
flag, K, index + 1, val_len)
# Return the maximum of all 3 cases
return max(full,
max(skip, half))
# Driver Code
value = [17, 20, 10, 15]
weight = [4, 2, 7, 5]
K = 1
W = 4
val_len = len(value)
print(maximum(value, weight, W,
0, K, 0, val_len))
# This code is contributed by sanjoy_62.
// C# implementation for the above approach
using System;
public class GFG {
// Function to find the maximum value
static int maximum(int[] value, int[] weight,
int weight1, int flag, int K,
int index)
{
// base condition
if (index >= value.Length) {
return 0;
}
// K elements already reduced
// to half of their weight
if (flag == K) {
// Dont include item
int skip = maximum(value, weight, weight1, flag,
K, index + 1);
int full = 0;
// If weight of the item is
// less than or equal to the
// remaining weight then include
// the item
if (weight[index] <= weight1) {
full = value[index]
+ maximum(value, weight,
weight1 - weight[index],
flag, K, index + 1);
}
// Return the maximum of
// both cases
return Math.Max(full, skip);
}
// If the weight reduction to half
// is possible
else {
// Skip the item
int skip = maximum(value, weight, weight1, flag,
K, index + 1);
int full = 0;
int half = 0;
// Include item with full weight
// if weight of the item is less
// than the remaining weight
if (weight[index] <= weight1) {
full = value[index]
+ maximum(value, weight,
weight1 - weight[index],
flag, K, index + 1);
}
// Include item with half weight
// if half weight of the item is
// less than the remaining weight
if (weight[index] / 2 <= weight1) {
half
= value[index]
+ maximum(value, weight,
weight1 - weight[index] / 2,
flag, K, index + 1);
}
// Return the maximum of all 3 cases
return Math.Max(full, Math.Max(skip, half));
}
}
// Driver code
public static void Main(String[] args)
{
int[] value = { 17, 20, 10, 15 };
int[] weight = { 4, 2, 7, 5 };
int K = 1;
int W = 4;
Console.WriteLine(
maximum(value, weight, W, 0, K, 0));
}
}
// This code is contributed by shikhasingrajput
<script>
// javascript implementation for the above approach
// Function to find the maximum value
function maximum(value,
weight , weight1,
flag , K , index)
{
// base condition
if (index >= value.length) {
return 0;
}
// K elements already reduced
// to half of their weight
if (flag == K) {
// Dont include item
var skip = maximum(value,
weight, weight1,
flag, K, index + 1);
var full = 0;
// If weight of the item is
// less than or equal to the
// remaining weight then include
// the item
if (weight[index] <= weight1) {
full = value[index]
+ maximum(
value, weight,
weight1 - weight[index], flag,
K, index + 1);
}
// Return the maximum of
// both cases
return Math.max(full, skip);
}
// If the weight reduction to half
// is possible
else {
// Skip the item
var skip = maximum(
value, weight,
weight1, flag,
K, index + 1);
var full = 0;
var half = 0;
// Include item with full weight
// if weight of the item is less
// than the remaining weight
if (weight[index] <= weight1) {
full = value[index]
+ maximum(
value, weight,
weight1 - weight[index],
flag, K, index + 1);
}
// Include item with half weight
// if half weight of the item is
// less than the remaining weight
if (weight[index] / 2 <= weight1) {
half = value[index]
+ maximum(
value, weight,
weight1 - weight[index] / 2,
flag, K, index + 1);
}
// Return the maximum of all 3 cases
return Math.max(full,
Math.max(skip, half));
}
}
// Driver code
var value = [ 17, 20, 10, 15 ];
var weight = [ 4, 2, 7, 5 ];
var K = 1;
var W = 4;
document.write(
maximum(value, weight, W,
0, K, 0));
// This code is contributed by Princi Singh
</script>
Output
37
Time Complexity: O(3^N)
Auxiliary Space: O(N)
Given problem is the variation of the 0-1 knapsack problem. In traditional 0-1 Knapsack problem, we only have to choices: skip the i-th item or take it.
However, in this case we will have 3 choices as mentioned in the recursive approach.
Efficient Approach (Dynamic Programming):
In Dynamic programming, we will work considering the same cases as above. We shall consider a 3D DP table where the state DP[i][j][k] will denote the maximum value we can obtain if we are considering values from 1 to i-th, weight of the knapsack is j and we can half the weight of at most k values. Basically, we are adding one extra state, the number of weights that can be halved in a traditional 2-D 01 knapsack DP matrix.
Now, three possibilities can take place:
- Include item with full weight if the item’s weight does not exceed the remaining weight
- Include the item with half weight if the item’s half weight does not exceed the remaining weight
- Skip the item
Now we have to take the maximum of these three possibilities. If we do not take the i-th weight then dp[i][j][k] would remain equal to dp[i - 1][j][k], just llike traditional knapsack. If we include item with half weight then dp[i][j][k] would be equal to dp[i - 1][j - wt[i] / 2][k - 1] + val[i] as after including i-th value our remaining knapsack capacity would be j - wt[i] / 2 and our number of half operations would increase by 1. Similarly, if we include item with full weight then dp[i][j][k] would be equal to dp[i - 1][j - wt[i]][k] + val[i] as knapsack capacity in this case would reduce to j - wt[i].
We simply take the maximum of all three choices.
Below is the code implementation of above approach:
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
int getMaximumNutrition(int W, vector<int> value, vector<int> weight, int x) {
int n=value.size();
int dp[n + 1][W + 1][x + 1];
/*
dp[i][j][k] denotes the maximum value that we can obtain if we are considering first i
elements of array, our capacity is j and we can use only k half operations
*/
memset(dp, 0, sizeof(dp));
for(int i = 1;i <= n; i++)
{
for(int j = 1;j <= W; j++)
{
int lim = x;
if(lim > i)
{
lim = i;
}
for(int k = 0;k <= lim; k++)
{
dp[i][j][k] = max(dp[i - 1][j][k], dp[i][j][k]);//skip
int val = j - (weight[i - 1] / 2);
if(val >= 0 && k > 0)//ensure that j-weight[i-1]/2 and k-1 don't become -ve
{
dp[i][j][k] = max(dp[i - 1][val][k - 1]+value[i - 1], dp[i][j][k]);
//take item applying half operation
}
val = j - weight[i - 1];
if(val >= 0)//ensure that j-weight[i-1] doesn't become -ve
{
dp[i][j][k] = max(dp[i - 1][val][k] + value[i - 1], dp[i][j][k]);
//take item without applying half operation
}
}
}
}
return dp[n][W][x];
}
int main() {
vector<int> value = {17, 20, 10, 15};
vector<int> weight = {4, 2, 7, 5};
int x = 1;
int W = 4;
cout << getMaximumNutrition(W, value, weight, x);
return 0;
}
import java.util.*;
public class Main {
static int getMaximumNutrition(int W, int[] value,
int[] weight, int x)
{
int n = value.length;
int[][][] dp = new int[n + 1][W + 1][x + 1];
/*
dp[i][j][k] denotes the maximum value that we can
obtain if we are considering first i elements of
array, our capacity is j and we can use only k half
operations
*/
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= W; j++) {
int lim = x;
if (lim > i) {
lim = i;
}
for (int k = 0; k <= lim; k++) {
dp[i][j][k]
= Math.max(dp[i - 1][j][k],
dp[i][j][k]); // skip
int val = j - (weight[i - 1] / 2);
if (val >= 0
&& k > 0) // ensure that
// j-weight[i-1]/2 and k-1
// don't become -ve
{
dp[i][j][k]
= Math.max(dp[i - 1][val][k - 1]
+ value[i - 1],
dp[i][j][k]);
// take item applying half operation
}
val = j - weight[i - 1];
if (val
>= 0) // ensure that j-weight[i-1]
// doesn't become -ve
{
dp[i][j][k]
= Math.max(dp[i - 1][val][k]
+ value[i - 1],
dp[i][j][k]);
// take item without applying half
// operation
}
}
}
}
return dp[n][W][x];
}
public static void main(String[] args)
{
int[] value = { 17, 20, 10, 15 };
int[] weight = { 4, 2, 7, 5 };
int x = 1;
int W = 4;
System.out.println(
getMaximumNutrition(W, value, weight, x));
}
}
// This code is contributed by garg28harsh.
using System;
class GFG {
static int getMaximumNutrition(int W, int[] value,
int[] weight, int x)
{
int n = value.Length;
int[, , ] dp = new int[n + 1, W + 1, x + 1];
/*
dp[i][j][k] denotes the maximum value that we can
obtain if we are considering first i elements of
array, our capacity is j and we can use only k half
operations
*/
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= W; j++) {
int lim = x;
if (lim > i) {
lim = i;
}
for (int k = 0; k <= lim; k++) {
dp[i, j, k]
= Math.Max(dp[i - 1, j, k],
dp[i, j, k]); // skip
int val = j - (weight[i - 1] / 2);
if (val >= 0
&& k > 0) // ensure that
// j-weight[i-1]/2 and k-1
// don't become -ve
{
dp[i, j, k]
= Math.Max(dp[i - 1, val, k - 1]
+ value[i - 1],
dp[i, j, k]);
// take item applying half operation
}
val = j - weight[i - 1];
if (val
>= 0) // ensure that j-weight[i-1]
// doesn't become -ve
{
dp[i, j, k]
= Math.Max(dp[i - 1, val, k]
+ value[i - 1],
dp[i, j, k]);
// take item without applying half
// operation
}
}
}
}
return dp[n, W, x];
}
static void Main()
{
int[] val = { 17, 20, 10, 15 };
int[] weight = { 4, 2, 7, 5 };
int x = 1;
int W = 4;
Console.Write(
getMaximumNutrition(W, val, weight, x));
}
}
// This code is contributed by garg28harsh.
function getMaximumNutrition(W, value, weight, x) {
let n=value.length;
/*
dp[i][j][k] denotes the maximum value that we can obtain if we are considering first i
elements of array, our capacity is j and we can use only k half operations
*/
let dp = new Array(n+1);
for(let i = 0; i < n + 1; i++)
{
dp[i] = new Array(W+1);
for(let j = 0; j < W + 1; j++)
{
dp[i][j] = new Array(x+1);
for(let k = 0; k < x + 1; k++)
{
dp[i][j][k] = 0;
}
}
}
for(let i = 1;i <= n; i++)
{
for(let j = 1;j <= W; j++)
{
let lim = x;
if(lim > i)
{
lim = i;
}
for(let k = 0;k <= lim; k++)
{
dp[i][j][k] = Math.max(dp[i - 1][j][k], dp[i][j][k]);//skip
let val = j - Math.round(weight[i - 1] / 2);
if(val >= 0 && k > 0)//ensure that j-weight[i-1]/2 and k-1 don't become -ve
{
dp[i][j][k] = Math.max(dp[i - 1][val][k - 1]+value[i - 1], dp[i][j][k]);
//take item applying half operation
}
val = j - weight[i - 1];
if(val >= 0)//ensure that j-weight[i-1] doesn't become -ve
{
dp[i][j][k] = Math.max(dp[i - 1][val][k] + value[i - 1], dp[i][j][k]);
//take item without applying half operation
}
}
}
}
return dp[n][W][x];
}
let value = [17, 20, 10, 15];
let weight = [4, 2, 7, 5];
let x = 1;
let W = 4;
console.log(getMaximumNutrition(W, value, weight, x));
// This code is contributed by garg28harsh.
# Python3 code for the above approach:
from typing import List, Tuple
def getMaximumNutrition(W: int, value: List[int], weight: List[int], x: int) -> int:
n = len(value)
dp = [[[0 for _ in range(x+1)] for _ in range(W+1)] for _ in range(n+1)]
for i in range(1, n+1):
for j in range(1, W+1):
lim = min(x, i)
for k in range(lim+1):
dp[i][j][k] = max(dp[i-1][j][k], dp[i][j][k]) #skip
val = j - (weight[i-1] // 2)
if val >= 0 and k > 0: #ensure that j-weight[i-1]/2 and k-1 don't become negative
dp[i][j][k] = max(dp[i-1][val][k-1] + value[i-1], dp[i][j][k]) # take item applying half operation
val = j - weight[i-1]
if val >= 0: #ensure that j-weight[i-1] doesn't become negative
dp[i][j][k] = max(dp[i-1][val][k] + value[i-1], dp[i][j][k]) # take item without applying half operation
return dp[n][W][x]
if __name__ == "__main__":
value = [17, 20, 10, 15]
weight = [4, 2, 7, 5]
x = 1
W = 4
print(getMaximumNutrition(W, value, weight, x))
Output
37
Time Complexity: O(n*W*K)
Auxiliary Space: O(n*W*K)
Note that space complexity can be further reduced to O(W*2*2) as for computing some dp[i][j][k] we only need to know values at current and previous i-th and k-th state. Time complexity will remain the same.