【问题标题】:Finding the minimum value找到最小值
【发布时间】:2020-10-17 15:38:27
【问题描述】:

我无法理解如何解决这个问题。有人可以帮我指出我如何处理它的方向吗?

N 个任务被分配,并且有 M 个工人可用。每个工人可以花费不同的时间来完成每项任务。给出了每个工人完成每项任务所花费的时间。在任何时候,只有一名工人可以完成一项任务。但条件是一旦工人停止工作,他就不能再从事任何任务。我想知道完成所有任务所需的最短时间是多少。这是一个例子-

M = 3
N = 4 {T1, T2,T3,T4}
每个工作人员为每个任务 (Ti) 所需的天数 (Wi) -

有很多方法可以完成任务,其中一些是 -

  1. 所有任务都由 W1 完成 ===> 总耗时 = 1+2+2+3 = 8
  2. 所有任务都由 W2 完成 ===> 总耗时 = 3+1+3+2 = 9
  3. 所有任务都由 W3 完成 ===> 总耗时 = 1+1+6+6 = 14
  4. T1、T2、T3 由 W1 完成,T4 由 W2 完成 ===> 总时间 = 1+2+2+2 = 7
  5. T1、T2 由 W1 完成,T3、T4 由 W3 完成 ===> 总耗时 = 1+2+6+6 = 15
  6. T1,T2 由 W3 完成,T3 由 W1 完成,T4 由 W2 完成 ===> 总时间 = 1+1+2+2 = 6

有更多可能的方法,但花费最少的方法是第 6 种方法(如下图所示)。

当工人人数只有2人时,我才能够理解如何做到这一点。我是这样做的-

#include<iostream>
using namespace std;

int N=4,M=2;

int main()
{   
    int i,j,min=INT_MAX;
    
    int sum,sum1;
    
    int w0[N] = {1,2,2,3};
    int w1[N] = {3,1,3,2};
    
    for(i=0;i<N;i++)
    {
        sum=0;
        sum1=0;
        for(j=0;j<i;j++)
        {
            sum+=w0[j];
            sum1+=w1[j];
        }
        for(j=N-1;j>=i;j--)
        {
            sum+=w1[j];
            sum1+=w0[j];
        }
        
        if(sum<sum1)
        {
            if(min>sum)
                min = sum;
        }
        else
        {
            if(min>sum1)
                min = sum1;
        }
    }
    
    cout<<min;
    
    return 0;
}

我试图用下面的另一个表格来解释它-

但是这样我只能找到 2 个工人的最小值。我需要帮助来了解超过 2 名工人的方法。

是否还有可能的 DP 解决方案?

【问题讨论】:

  • 你可以做的是创建一个矩阵,你可以在其中找到每个 [i][j] 的最小值。
  • 你必须按照给定的顺序完成任务还是可以重新排序?
  • @Joni no tasks can't be reordered
  • @Carlos1232 你的意思是每个工人的最短时间吗?这有什么帮助?

标签: c++ algorithm recursion math dynamic-programming


【解决方案1】:

我认为解决此问题的最佳方法是使用递归。我将通过一个不可用的工作人员列表和一个传递给每个调用的运行总和以及一个最小值的全局变量来实现这一点。

如果您有一个值矩阵,这也将最有效。所以就像matrix[0] = {1, 2, 3}; matrix[1] = {3, 4, 5}。我已经有一段时间没有对矩阵进行硬编码了,所以如果语法有点不对,请原谅我。

因此,对矩阵使用全局变量会类似于

int matrix[m][n];
int runningMinimum = INT_MAX; //set the runningMinimum to max so any value compared will be lower
void minimum(int i, vector<int> bannedWorkers, int currentWorker, int sum){
    //test the end condition here
    if (i == n-1){//last column
        if (sum < runningMinimum){runningMinimum = sum;}
        return; //we want to return at the end, whether it's found the new lowest value or not
   
    //if we're not at the end, we need to move one step to the right for all possible workers
    for (int j = 0; j < m; j++){//For each worker
        
        //check to see if the worker is no longer allowed to work
        bool isBanned = false
        for (int k = 0; k < bannedWorkers.size(); k++){
            if (bannedWorkers[k] == j) isBanned = true;
        }
        if(!isBanned){
            if (j == currentWorker){
                minimum(i+1, bannedWorkers, currentWorker, sum+matrix[j][i])
            }else{
                vector<int> newBannedWorkers = bannedWorkers; //Make sure to copy to a new vector
                newBannedWorkers.push_back(currentWorker);
                minimum(i+1, newBannedWorkers, j, sum + matrix[j][i])
            }
        }
    }
    return; //after we've checked every option we want to end that call
}

这是一个粗略的、未经检验的想法,但它应该会给你一个坚实的开端。希望对您有所帮助!

【讨论】:

  • 这是一个好方法,但您的解决方案是假设工人的排序顺序,即严格让第一个工人先工作,然后是第二个,然后是第三个。无论如何,工人都可以重新排序
  • 例如,如果 matrix[0] = {1,2,2,3} 和 matrix[1] = {3,1,3,2} 那么它给出了正确的结果。但是如果我们让 matrix[0] = {3,1,3,2} 和 matrix[1] = {1,2,2,3} 那么它会失败,因为它严格遵循 W0 必须在 W1 之前工作的顺序。在这种情况下,W1 必须首先完成任务 1-3,然后 W0 必须完成任务 4。无论如何,工人都可以重新排序。知道如何考虑所有可能的方式吗?
  • 它应该能够以任何顺序处理。只要确保为所有可能的开始调用它。例如``` int main(){ for(int j = 0; j bannedWorkers;最小值(0,bannedWorkers,j,矩阵[j][0])}返回0; } ```
  • 是的,我的错,我在调用它们时忘记更改 currentWorker。
  • 但是这个递归需要很多时间。我想知道我们是否可以以某种方式使用动态编程来保存我们已经计算过的值,以便以后需要时使用它们。
【解决方案2】:

如果工人数量很大,可能不是最好的方法,但我认为易于理解和实施。我会:

  1. 列出所有可能的 W 重复组合,例如使用 https://www.geeksforgeeks.org/combinations-with-repetitions/ 中的算法。这会给你类似 [[W1,W3,W2,W3,W1],[W3,W5,W5,W4,W5]...

  2. 丢弃工人不连续的组合(遍历列表,计算每个工人出现的总次数和连续出现的次数,如果不同则丢弃列表)

  3. 使用过滤的列表列表来检查使用表格的时间并保留最小的一个

丢弃列表的可能方法是

bool isValid=true;
for (int kk = 0; kk < workerOrder.Length; kk++)
    {    
        int state=0;
        for (int mm = 0; mm < workerOrder.Length; mm++)
        {
            if (workerOrder[mm] == kk && state == 0) { state = 1; } //it has appeard
            if (workerOrder[mm] != kk && state == 1 ) { state = 2; } //it is not contious
            if (workerOrder[mm] == kk && state == 2) { isValid = false; break; } //it appeard again
        }
        if (isValid==false){break;}
    }

【讨论】:

  • 是的,这很容易理解,但是检查所有可能的重复组合将花费大量时间(求和 n+r-1Cr),对于像 n=10 这样较小的值,检查超过 1.5 k 次。
猜你喜欢
  • 1970-01-01
  • 2012-06-21
  • 1970-01-01
  • 2010-09-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多