Tìm các công việc theo lịch trình không chồng chéo với chi phí tối đa


8

Cho một tập hợp n công việc với [thời gian bắt đầu, thời gian kết thúc, chi phí] tìm một tập hợp con sao cho không có 2 công việc nào trùng nhau và chi phí là tối đa.

Bây giờ tôi không chắc liệu một thuật toán tham lam có làm được điều đó không. Đó là, sắp xếp theo chi phí và luôn đảm nhận công việc tiếp theo không giao nhau và với chi phí tối đa giữa hai người.

Đây có phải là tương đương với một vấn đề ba lô? Làm thế nào tôi có thể tiếp cận nó?

Câu trả lời:


8

Biểu đồ của các công việc chồng chéo là một biểu đồ khoảng . Đồ thị khoảng là đồ thị hoàn hảo. Vì vậy, những gì bạn đang cố gắng làm là tìm một tập độc lập trọng lượng tối đa (nghĩa là không có hai phần trùng nhau) trong một biểu đồ hoàn hảo . Điều này có thể được giải quyết trong thời gian đa thức. Thuật toán được đưa ra trong "Thuật toán đa thức cho đồ thị hoàn hảo" của M. Grötschel, L. Lovász và A. Schrijver.

Có một số trường hợp đặc biệt về đồ thị hoàn hảo mà mọi người đã phát hiện ra các thuật toán đơn giản và hiệu quả hơn cho nhiệm vụ này. Tôi không biết liệu đồ thị khoảng có rơi vào một trong những trường hợp đặc biệt này không.


6

Thuật toán tham lam không thể giúp đỡ trong trường hợp đó. Và nó không thể được so sánh với cả hai vấn đề ba lô hoặc 0-1. Đầu tiên có thể được giải quyết bằng thuật toán tham lam trong O (n) và thứ hai là NP.

Vấn đề bạn gặp phải có thể bị ép buộc trong O (2 ^ n). Nhưng bạn có thể tối ưu hóa nó bằng cách sử dụng lập trình động.

1) Sắp xếp các khoảng thời gian theo thời gian bắt đầu.

2) Khởi tạo int [] cost = new int [jobs.length] bằng Integer.MIN_VALUE (hoặc bất kỳ giá trị âm nào);

3) Xác định theo thói quen đệ quy (ở đây là Java):

private int findCost(Job[] jobs, int k, int[] costs) {
   if(k >= jobs.length) {
      return 0;
   }
   if(costs[k] < 0) {
     int x = findNextCompatibleJob(jobs, k);
     int sumK = jobs[k].cost + findCost(jobs, x, costs);
     int sumK1 = findCost(jobs, k + 1, costs);
     costs[k] = Math.max(sumK, sumK1);
   }
   return costs[k];
}

private int findNextCompatibleJob(Job[] jobs, int k) {
   int finish = jobs[k].finish;
   for(int i = k + 1; i < jobs.length; i++) {
     if(jobs[i].start > finish) {
        return i;
     }
   }
   return Integer.MAX_VALUE;
}

4) Bắt đầu đệ quy với k = 0;

Tôi đã thực hiện chỉ quy trình đệ quy trong khi các phần khác là tầm thường. Tôi đã xem xét rằng bất kỳ chi phí nào là> = 0. Nếu có thể có các công việc chi phí âm, chúng ta cần thêm kiểm tra cho điều đó và chỉ cần vượt qua các công việc đó mà không cần xem xét.


5

Người ta có thể thực hiện điều này trong O (nlogn)

Các bước:

  1. Sắp xếp các khoảng dựa trên thời gian kết thúc
  2. xác định p (i) cho mỗi khoảng, cho điểm kết thúc lớn nhất nhỏ hơn điểm bắt đầu của khoảng thứ i. Sử dụng tìm kiếm nhị phân để có được nlogn
  3. xác định d [i] = max (w (i) + d [p (i)], d [i-1]).

khởi tạo d [0] = 0

Kết quả sẽ là d [n] n- số lượng khoảng.

Độ phức tạp tổng thể O (nlogn)

import java.util.*;
class Interval {
  public int start;
  public int end;
  public int cost;
  public Interval(int start, int end, int cost){
    this.start = start;
    this.end = end;
    this.cost = cost;
  }
}
public class BestCombinationFinder {
  public int getBestCombination(List<Interval> intervals) {
    if (intervals == null || intervals.size() == 0) {
      return 0;
    }
    Collections.sort(intervals, new Comparator<Interval>() {
      public int compare(Interval i1, Interval i2) {
        if (i1.end < i2.end) {
          return -1;
        }
        else if (i1.end > i2.end) {
          return 1;
        }
        return 0;
      }
    });
    return findBestCombination(intervals);
  }
  private int findBestCombination(List<Interval> intervals) {
    int[] dp = new int[intervals.size() + 1];
    for (int i = 1; i <= intervals.size(); i++) {
      Interval currInt = intervals.get(i - 1);
      int pIndex = find(intervals, currInt.start, 0, intervals.size() - 1);
      dp[i] = Math.max(dp[pIndex+1] + currInt.cost, dp[i - 1]);
    }
    return dp[intervals.size()];
  }
  private int find(List<Interval> intervals, int target, int left, int right) {
    if (left > right) {
      return right;
    }
    else {
      int mid = (left + right) / 2;
      if (intervals.get(mid).end == target) {
        return mid;
      }
      else if (intervals.get(mid).end > target) {
        return find(intervals, target, left, mid - 1);
      }
      else {
        return find(intervals, target, mid + 1, right);
      }
    }
  }
}

2
Vui lòng cung cấp mã giả, thay vì yêu cầu mọi người hiểu Java (và đặc biệt là các bộ sưu tập Java).
David Richerby

2
Tôi đã thêm mã giả trong phần đầu tiên của câu trả lời. Tôi chỉ muốn thêm mã Java tương ứng nếu nó giúp mọi người hiểu rõ hơn về nó.
Szilard Mandici

0

Vâng, nó tương đương với một vấn đề ba lô. Xem xét thời gian kết thúc công việc và chuẩn bị bàn như một chiếc ba lô. Trước khi đọc giải pháp sau đây, vui lòng kiểm tra vấn đề về chiếc ba lô và giải pháp của nó.

// Input:
// Jobs (stored in array jobs)
// Number of jobs (n)

find the maximum end time from given n jobs => max_end

for j from 0 to max_end do
         table[0, j] := 0
end for 

for i from 1 to n do
    for j from 0 to max_end do
        if jobs[i].end <= j then
           table[i, j] := max(table[i-1, j], table[i-1, jobs[i].start] + jobs[i].cost)
       else
           table[i, j] := table[i-1, j]
       end if
    end for
 end for

Bạn cũng có thể in các công việc được lên lịch bằng cách duyệt qua bảng:

j := max_end;
for i from n to 1 do
    if table[i][j] != table[i-1][j]
        print jobs[i]
        j = jobs[i].start; 
    end if
end for

Sự phức tạp cũng giống như vấn đề ba lô.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.