#include<iostream>
#include<vector>
#include<climits>
#include<cstring>
#include<algorithm>
using namespace std;
// Basic structure for the given problem
struct Item {
float weight;
float volume;
Item(float weight, float volume) {
this->weight = weight;
this->volume = volume;
}
bool operator<(const Item &other) const {
if(weight == other.weight) {
return volume < other.volume;
}
return weight < other.weight;
}
};
// Some constant values
const static int INF = INT_MAX / 100;
const static int MAX_NUM_OF_ITEMS = 1000;
const static int MAX_N = 1000;
// Parameters that we define in main()
float MAX_VOLUME;
vector<Item> items;
// DP lookup tables
int till[MAX_NUM_OF_ITEMS];
float dp[MAX_NUM_OF_ITEMS][MAX_N];
/**
* curIndex: the starting index from where we aim to formulate a new group
* left: number of groups still left to be formed
*/
float solve(int curIndex, int left) {
// Baseline condition
if(curIndex >= items.size() && left == 0) {
return 0;
}
if(curIndex >= items.size() && left != 0) {
return INF;
}
// If we have no more groups to be found, but there still are items left
// then invalidate the solution by returning INF
if(left <= 0 && curIndex < items.size()) {
return INF;
}
// Our lookup dp table
if(dp[curIndex][left] >= 0) {
return dp[curIndex][left];
}
// minVal is the metric to optimize which is the `sum of the differences
// for each group` we intialize it as INF
float minVal = INF;
// The volume of the items we're going to pick for this group
float curVolume = 0;
// Let's try to see how large can this group be by trying to expand it
// one item at a time
for(int i = curIndex; i < items.size(); i++) {
// Verfiy we can put the item i in this group or not
if(curVolume + items[i].volume > MAX_VOLUME) {
break;
}
curVolume += items[i].volume;
// Okay, let's see if it's possible for this group to exist
float val = (items[i].weight - items[curIndex].weight) + solve(i + 1, left - 1);
if(minVal >= val) {
minVal = val;
// The lookup table till tells that the group starting at index
// curIndex, expands till i, i.e. [curIndex, i] is our valid group
till[curIndex] = i + 1;
}
}
// Store the result in dp for memoization and return the value
return dp[curIndex][left] = minVal;
}
int main() {
// The maximum value for Volume
MAX_VOLUME = 6;
// The number of groups we need
int NUM_OF_GROUPS = 5;
items = vector<Item>({
// Item(weight, volume)
Item(5, 2),
Item(2, 1),
Item(10, 3),
Item(7, 2),
Item(3, 1),
Item(5, 3),
Item(4, 3),
Item(3, 2),
Item(10, 1),
Item(11, 3),
Item(19, 1),
Item(21, 2)
});
// Initialize the dp with -1 as default value for unexplored states
memset(dp, -1, sizeof dp);
// Sort the items based on weights first
sort(items.begin(), items.end());
// Solve for the given problem
int val = solve(0, NUM_OF_GROUPS);
// If return value is INF, it means we couldn't distribute it in n
// groups due to the contraint on volume or maybe the number of groups
// was greater than the number of items we had ^_^
if(val >= INF) {
cout << "Not possible to distribute in " << NUM_OF_GROUPS;
return 0;
}
// If a solution exists, use the lookup till array to find which items
// belong to which set
int curIndex = 0, group = 1;
while(curIndex < items.size()) {
cout << "Group #" << group << ": ";
for(int i = curIndex; i < till[curIndex]; i++)
cout << "(" << items[i].weight << ", " << items[i].volume << ") ";
cout << '\n';
group++;
curIndex = till[curIndex];
}
}
I2luY2x1ZGU8aW9zdHJlYW0+CiNpbmNsdWRlPHZlY3Rvcj4KI2luY2x1ZGU8Y2xpbWl0cz4KI2luY2x1ZGU8Y3N0cmluZz4KI2luY2x1ZGU8YWxnb3JpdGhtPgp1c2luZyBuYW1lc3BhY2Ugc3RkOwoKLy8gQmFzaWMgc3RydWN0dXJlIGZvciB0aGUgZ2l2ZW4gcHJvYmxlbQpzdHJ1Y3QgSXRlbSB7CglmbG9hdCB3ZWlnaHQ7CglmbG9hdCB2b2x1bWU7CgkKCUl0ZW0oZmxvYXQgd2VpZ2h0LCBmbG9hdCB2b2x1bWUpIHsKCQl0aGlzLT53ZWlnaHQgPSB3ZWlnaHQ7CgkJdGhpcy0+dm9sdW1lID0gdm9sdW1lOwoJfQoJCglib29sIG9wZXJhdG9yPChjb25zdCBJdGVtICZvdGhlcikgY29uc3QgewoJCWlmKHdlaWdodCA9PSBvdGhlci53ZWlnaHQpIHsKCQkJcmV0dXJuIHZvbHVtZSA8IG90aGVyLnZvbHVtZTsKCQl9CgkJcmV0dXJuIHdlaWdodCA8IG90aGVyLndlaWdodDsKCX0KfTsKCi8vIFNvbWUgY29uc3RhbnQgdmFsdWVzCmNvbnN0IHN0YXRpYyBpbnQgSU5GID0gSU5UX01BWCAvIDEwMDsKY29uc3Qgc3RhdGljIGludCBNQVhfTlVNX09GX0lURU1TID0gMTAwMDsKY29uc3Qgc3RhdGljIGludCBNQVhfTiA9IDEwMDA7CgovLyBQYXJhbWV0ZXJzIHRoYXQgd2UgZGVmaW5lIGluIG1haW4oKQpmbG9hdCBNQVhfVk9MVU1FOwp2ZWN0b3I8SXRlbT4gaXRlbXM7CgovLyBEUCBsb29rdXAgdGFibGVzCmludCB0aWxsW01BWF9OVU1fT0ZfSVRFTVNdOwpmbG9hdCBkcFtNQVhfTlVNX09GX0lURU1TXVtNQVhfTl07CgovKioKICogY3VySW5kZXg6IHRoZSBzdGFydGluZyBpbmRleCBmcm9tIHdoZXJlIHdlIGFpbSB0byBmb3JtdWxhdGUgYSBuZXcgZ3JvdXAKICogbGVmdDogbnVtYmVyIG9mIGdyb3VwcyBzdGlsbCBsZWZ0IHRvIGJlIGZvcm1lZAogKi8gCmZsb2F0IHNvbHZlKGludCBjdXJJbmRleCwgaW50IGxlZnQpIHsKCS8vIEJhc2VsaW5lIGNvbmRpdGlvbgoJaWYoY3VySW5kZXggPj0gaXRlbXMuc2l6ZSgpICYmIGxlZnQgPT0gMCkgewoJCXJldHVybiAwOwoJfQoJaWYoY3VySW5kZXggPj0gaXRlbXMuc2l6ZSgpICYmIGxlZnQgIT0gMCkgewoJCXJldHVybiBJTkY7Cgl9CgkvLyBJZiB3ZSBoYXZlIG5vIG1vcmUgZ3JvdXBzIHRvIGJlIGZvdW5kLCBidXQgdGhlcmUgc3RpbGwgYXJlIGl0ZW1zIGxlZnQKCS8vIHRoZW4gaW52YWxpZGF0ZSB0aGUgc29sdXRpb24gYnkgcmV0dXJuaW5nIElORgoJaWYobGVmdCA8PSAwICYmIGN1ckluZGV4IDwgaXRlbXMuc2l6ZSgpKSB7CgkJcmV0dXJuIElORjsKCX0KCQoJLy8gT3VyIGxvb2t1cCBkcCB0YWJsZQoJaWYoZHBbY3VySW5kZXhdW2xlZnRdID49IDApIHsKCQlyZXR1cm4gZHBbY3VySW5kZXhdW2xlZnRdOwoJfQoJCgkvLyBtaW5WYWwgaXMgdGhlIG1ldHJpYyB0byBvcHRpbWl6ZSB3aGljaCBpcyB0aGUgYHN1bSBvZiB0aGUgZGlmZmVyZW5jZXMKCS8vIGZvciBlYWNoIGdyb3VwYCB3ZSBpbnRpYWxpemUgaXQgYXMgSU5GCglmbG9hdCBtaW5WYWwgPSBJTkY7CgkKCS8vIFRoZSB2b2x1bWUgb2YgdGhlIGl0ZW1zIHdlJ3JlIGdvaW5nIHRvIHBpY2sgZm9yIHRoaXMgZ3JvdXAKCWZsb2F0IGN1clZvbHVtZSA9IDA7CgkKCS8vIExldCdzIHRyeSB0byBzZWUgaG93IGxhcmdlIGNhbiB0aGlzIGdyb3VwIGJlIGJ5IHRyeWluZyB0byBleHBhbmQgaXQgCgkvLyBvbmUgaXRlbSBhdCBhIHRpbWUKCWZvcihpbnQgaSA9IGN1ckluZGV4OyBpIDwgaXRlbXMuc2l6ZSgpOyBpKyspIHsKCQkvLyBWZXJmaXkgd2UgY2FuIHB1dCB0aGUgaXRlbSBpIGluIHRoaXMgZ3JvdXAgb3Igbm90CgkJaWYoY3VyVm9sdW1lICsgaXRlbXNbaV0udm9sdW1lID4gTUFYX1ZPTFVNRSkgewoJCQlicmVhazsKCQl9CgkJY3VyVm9sdW1lICs9IGl0ZW1zW2ldLnZvbHVtZTsKCQkvLyBPa2F5LCBsZXQncyBzZWUgaWYgaXQncyBwb3NzaWJsZSBmb3IgdGhpcyBncm91cCB0byBleGlzdAoJCWZsb2F0IHZhbCA9IChpdGVtc1tpXS53ZWlnaHQgLSBpdGVtc1tjdXJJbmRleF0ud2VpZ2h0KSArIHNvbHZlKGkgKyAxLCBsZWZ0IC0gMSk7CgkJaWYobWluVmFsID49IHZhbCkgewoJCQltaW5WYWwgPSB2YWw7CgkJCS8vIFRoZSBsb29rdXAgdGFibGUgdGlsbCB0ZWxscyB0aGF0IHRoZSBncm91cCBzdGFydGluZyBhdCBpbmRleAoJCQkvLyBjdXJJbmRleCwgZXhwYW5kcyB0aWxsIGksIGkuZS4gW2N1ckluZGV4LCBpXSBpcyBvdXIgdmFsaWQgZ3JvdXAKCQkJdGlsbFtjdXJJbmRleF0gPSBpICsgMTsKCQl9Cgl9CgkvLyBTdG9yZSB0aGUgcmVzdWx0IGluIGRwIGZvciBtZW1vaXphdGlvbiBhbmQgcmV0dXJuIHRoZSB2YWx1ZQoJcmV0dXJuIGRwW2N1ckluZGV4XVtsZWZ0XSA9IG1pblZhbDsKfQoKaW50IG1haW4oKSB7CgkvLyBUaGUgbWF4aW11bSB2YWx1ZSBmb3IgVm9sdW1lCglNQVhfVk9MVU1FID0gNjsKCS8vIFRoZSBudW1iZXIgb2YgZ3JvdXBzIHdlIG5lZWQKCWludCBOVU1fT0ZfR1JPVVBTID0gNTsKCglpdGVtcyA9IHZlY3RvcjxJdGVtPih7CgkvLyBJdGVtKHdlaWdodCwgdm9sdW1lKQoJCUl0ZW0oNSwgMiksCgkJSXRlbSgyLCAxKSwKCQlJdGVtKDEwLCAzKSwKCQlJdGVtKDcsIDIpLAoJCUl0ZW0oMywgMSksCgkJSXRlbSg1LCAzKSwKCQlJdGVtKDQsIDMpLAoJCUl0ZW0oMywgMiksCgkJSXRlbSgxMCwgMSksCgkJSXRlbSgxMSwgMyksCgkJSXRlbSgxOSwgMSksCgkJSXRlbSgyMSwgMikKCX0pOwoJCgkvLyBJbml0aWFsaXplIHRoZSBkcCB3aXRoIC0xIGFzIGRlZmF1bHQgdmFsdWUgZm9yIHVuZXhwbG9yZWQgc3RhdGVzCgltZW1zZXQoZHAsIC0xLCBzaXplb2YgZHApOwoJCgkvLyBTb3J0IHRoZSBpdGVtcyBiYXNlZCBvbiB3ZWlnaHRzIGZpcnN0Cglzb3J0KGl0ZW1zLmJlZ2luKCksIGl0ZW1zLmVuZCgpKTsKCQoJLy8gU29sdmUgZm9yIHRoZSBnaXZlbiBwcm9ibGVtCglpbnQgdmFsID0gc29sdmUoMCwgTlVNX09GX0dST1VQUyk7CgkKCS8vIElmIHJldHVybiB2YWx1ZSBpcyBJTkYsIGl0IG1lYW5zIHdlIGNvdWxkbid0IGRpc3RyaWJ1dGUgaXQgaW4gbgoJLy8gZ3JvdXBzIGR1ZSB0byB0aGUgY29udHJhaW50IG9uIHZvbHVtZSBvciBtYXliZSB0aGUgbnVtYmVyIG9mIGdyb3VwcwoJLy8gd2FzIGdyZWF0ZXIgdGhhbiB0aGUgbnVtYmVyIG9mIGl0ZW1zIHdlIGhhZCBeX14KCWlmKHZhbCA+PSBJTkYpIHsKCQljb3V0IDw8ICJOb3QgcG9zc2libGUgdG8gZGlzdHJpYnV0ZSBpbiAiIDw8IE5VTV9PRl9HUk9VUFM7CgkJcmV0dXJuIDA7Cgl9CgkKCS8vIElmIGEgc29sdXRpb24gZXhpc3RzLCB1c2UgdGhlIGxvb2t1cCB0aWxsIGFycmF5IHRvIGZpbmQgd2hpY2ggaXRlbXMKCS8vIGJlbG9uZyB0byB3aGljaCBzZXQJCglpbnQgY3VySW5kZXggPSAwLCBncm91cCA9IDE7Cgl3aGlsZShjdXJJbmRleCA8IGl0ZW1zLnNpemUoKSkgewoJCWNvdXQgPDwgIkdyb3VwICMiIDw8IGdyb3VwIDw8ICI6ICI7CgkJZm9yKGludCBpID0gY3VySW5kZXg7IGkgPCB0aWxsW2N1ckluZGV4XTsgaSsrKQoJCQljb3V0IDw8ICIoIiA8PCBpdGVtc1tpXS53ZWlnaHQgPDwgIiwgIiA8PCBpdGVtc1tpXS52b2x1bWUgPDwgIikgIjsKCQljb3V0IDw8ICdcbic7CgkJZ3JvdXArKzsJCgkJY3VySW5kZXggPSB0aWxsW2N1ckluZGV4XTsKCX0KfQo=