问题如下:
您有n个行程长度(以km为单位),应该在m天之间进行划分,以使每天最大长度总和最小化。例如。在3天内划分的行程长度[1,5,2,6,8,3,2]导致[1,5,2] [6] [8,3,2],因为日长度的最大值是我们能达到的最低点。
是否有一种描述处理此类问题的算法?我遇到了bin包装和背包问题,但没有一个能解决我的问题。我可以想象这可能是对箱子包装的一点修改但是没有得出结论。
答案 0 :(得分:4)
使用二进制搜索
可以解决此问题假设最大长度为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 是所有行程的总和。
答案 1 :(得分:3)
可以使用动态编程方法解决,其中状态定义为DP[i][j]
,其中i
表示一天的结束索引,j
保留日期的计数远。您可以移动到下一个状态并获取与当前移动相对应的一天的最大总和,然后将其与总体最小值进行比较。
我已经用c ++编写了一个递归动态编程解决方案,关于状态转换是如何工作可能有点难以理解你可能需要通过memoisation来了解动态编程以理解它。
#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 :(得分:3)
它既不是背包也不是Bin Packing。它的常见名称是k分区问题 正如评论中所提到的,这可以通过使用动态编程来完成。
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))