簡體   English   中英

將一個列表分為n個不同的匹配條件的子列表,同時保持盡可能接近的狀態

[英]Split a list into n different sublists matching condition while remaining as close as possible

我的問題與這個問題有很多共同點: 將數字列表分成n個塊,以使這些塊具有(接近)相等的總和並保持原始順序

主要區別在於我有一個略有不同的指標來確定哪個分割是“最佳”的,並且在這樣做時我有一個任意的條件要遵守。

我列表中的每個項目都有兩個組成部分。 重量和體積。 我必須將它們分為n個不同的子組,同時使每個子組的總權重盡可能接近。 測試的方法就是簡單地獲得最重和最輕的子組之間的差異。 該差異越小越好。 這意味着子組[15] [15] [15] [10]在最終分數上的價值與子組[15] [13] [11] [10]相同。

然后,這是我無法弄清楚如何添加到作為鏈接問題的答案而提出的算法中的部分,我有一個必須遵守的艱苦條件。 每個子組都有一個最大音量[v],沒有一個可以超過它。 超過該值不會降低分數,它會使整個答案無效。

如何適應用作前一種答案的算法(和代碼片段)以考慮到音量條件和略有不同的評分方法?

我正在尋找代碼,偽代碼或如何做到的書面(詳細)說明。 這個問題被標記為C#,因為這就是我正在使用的語言,但是我相信我可以從任何非深奧的語言進行翻譯,因此,如果您使用代碼進行回答,請隨意使用。

正如另一個問題中提到的那樣,此問題非常復雜,在合理的計算時間內無法找到最佳解決方案,因此,我正在尋找一個給出“足夠好”解決方案的答案,即使它可能不是最佳解決方案。

我使用動態編程為給定的問題制定了確定性的解決方案,共享同一https://ideone.com/pkfyxg的代碼

#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];
    }
}

我已經在代碼中添加了注釋,以幫助您了解它的工作效果。 相同的總體運行時復雜度為O(num_of_groups *(num_of_items) 2 )讓我知道您是否需要對相同的^^進行更多解釋;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM