【问题标题】:What kind of algorithm? (Knapsack, Bin Packing!?)什么样的算法? (背包,装箱!?)
【发布时间】:2016-12-29 23:49:30
【问题描述】:

问题如下:

您有 n 个以 km 为单位的行程长度,应将其划分为 m 天,以使每天的最大行程总和最小化。例如。行程长度 [1,5,2,6,8,3,2] 分为 3 天,结果为 [1,5,2] [6] [8,3,2],因为日长总和的最大值为我们能达到的最低点。

是否有一种算法可以描述处理此类问题的方法?我遇到了垃圾箱包装和背包问题,但没有一个能解决我的问题。我可以想象这可能是对垃圾箱包装的一些修改,但不要得出结论。

【问题讨论】:

  • It 动态规划问题,可以在O(n * m)解决
  • 这是大学作业还是其他问卷中的问题?
  • 好吧,这个问题没有很好的定义......例如,一个更好的解决方案,然后提出的一个是:[1,5,2,6,8,3,2], [], [],其中最短日长为 0,优于 6。无论如何,在一个简单的解决方案中,您可以只使用 binpacking 并对卷参数使用二进制搜索。
  • @Bakuriu 我认为他希望将m 值中的最大值最小化。
  • 对不起,我忘了一句话。我需要最小化每天的最大长度总和。

标签: algorithm knapsack-problem bin-packing


【解决方案1】:

它可以使用动态编程方法来解决,其中状态定义为DP[i][j],其中i 指的是一天的结束索引,j 保留到目前为止的天数。您可以移动到下一个状态并获取与当前移动相对应的一天的最大总和,然后可以将其与总体最小值进行比较。

我已经用 c++ 编写了一个递归动态编程解决方案,可能有点难以理解状态转换是如何工作的,您可能需要研究带记忆的动态编程才能理解它。

#include <iostream>
#define INF 1000000000
using namespace std;

int n, m, dist[100] = {1,5,2,6,8,3,2}, dp[1000][1000];

int solve(int index, int count){
    if(index == n){
        if(count == m) return 0;
        else return INF;
    }
    if(dp[index][count] != -1) return dp[index][count];
    int sum = 0, ans = INF;
    for(int i = index;i < n;i++){
        sum += dist[i];
        int val = max(sum, solve(i+1, count+1));
        ans = min(ans, val);
    }
    return dp[index][count] = ans;
}

int main() {
    // your code goes here
    n = 7, m = 3;
    for(int i = 0;i < 1000;i++) for(int j = 0;j < 1000;j++) dp[i][j] = -1;
    cout << solve(0, 0) << endl;
    return 0;
}

Ideone 解决方案链接:https://ideone.com/glHsgF

【讨论】:

    【解决方案2】:

    既不是背包也不是垃圾箱。它的通称是k-partition问题。
    如 cmets 中所述,这可以通过使用动态编程来完成。

    DP[N,M] - minimum cost of partitioning the array = {a1, ... , aN} into M partitions.
              Where cost is defined as the maximum sum of each partition.
    DP[1,m] = a1
    DP[n,1] = Sum of all elements in the array {a1, ... , an}
    DP[n,m] = min over k from 1 to n ( max(DP[n-k,m-1],sum of elements n-k to n))
    

    【讨论】:

      【解决方案3】:

      这个问题可以用二分查找来解决

      假设最大长度为X,我们可以很容易地通过以下贪心方法检查是否可以将行程分成m天,每天的最大长度不大于X

      boolean isValid(int X){
         int currentLength = 0;
         int numberOfDays = 1;
         for(int i = 0; i < n; i++)
            if (trip[i] > X)
               return false;
            if(currentLength + trip[i] <= X){
               currentLength += trip[i];  
            }else{
               currentLength = trip[i];
               numberOfDays++;
            }
         }
         return numberOfDays <= m;
      }
      

      那么,我们可以很容易地通过下面的二分搜索找到 X 的最小值:

      int start = 0;
      int end = sum of all trips;
      int result = end;
      while(start <=end){
          int mid = (start + end)/2;
          if(isValid(mid)){
             result = mid;
             end = mid - 1;
          }else{
             start = mid + 1;
          }
      }
      

      时间复杂度为 O(n log z),其中 z 是所有行程的总和。

      【讨论】:

      • 哇,这是一个非常好的解决方案!
      • 非常好,你能简要解释一下为什么这个贪婪有效吗?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-01
      • 1970-01-01
      • 2012-11-18
      • 1970-01-01
      • 1970-01-01
      • 2021-08-01
      相关资源
      最近更新 更多