Trova lavori pianificati non sovrapposti con il costo massimo


8

Dato un insieme di n lavori con [ora di inizio, ora di fine, costo] trova un sottoinsieme in modo che non vi siano 2 lavori sovrapposti e il costo sia massimo.

Ora non sono sicuro se un algoritmo avido farà il trucco. Cioè, ordina per costo e prendi sempre il lavoro successivo che non si interseca e con il costo massimo tra i due.

È equivalente a un problema con lo zaino? Come potrei avvicinarmi?

Risposte:


8

Il grafico dei lavori sovrapposti è un grafico a intervalli . I grafici a intervalli sono grafici perfetti. Quindi quello che stai cercando di fare è trovare un set indipendente dal peso massimo (cioè, non ci sono due sovrapposizioni) in un grafico perfetto . Questo può essere risolto in tempo polinomiale. L'algoritmo è presentato in "Algoritmi polinomiali per grafi perfetti" , di M. Grötschel, L. Lovász e A. Schrijver.

Esistono numerosi casi speciali di grafici perfetti per i quali le persone hanno scoperto algoritmi più semplici ed efficienti per questo compito. Non so se i grafici a intervalli rientrino in uno di questi casi speciali.


6

L'algoritmo goloso non può essere d'aiuto in quel caso. E non poteva essere paragonato a problemi di zaino frazionari o 0-1. Il primo potrebbe essere risolto da un avido algoritmo in O (n) e il secondo è NP.

Il problema che hai potrebbe essere forzato brutalmente in O (2 ^ n). Ma potresti ottimizzarlo usando la programmazione dinamica.

1) Ordina gli intervalli in base all'ora di inizio.

2) Inizializza int [] cost = new int [jobs.length] con Integer.MIN_VALUE (o qualsiasi valore negativo);

3) Definire seguire la routine ricorsiva (qui è 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) Inizia la ricorsione con k = 0;

Ho implementato solo la routine di ricorsione mentre altre parti sono banali. Ho considerato che qualsiasi costo è> = 0. Se ci possono essere lavori a costo negativo, dobbiamo aggiungere un controllo per quello e passare semplicemente quei lavori senza considerazione.


5

Si potrebbe implementare questo in O (nlogn)

passi:

  1. Ordinare gli intervalli in base all'ora di fine
  2. definire p (i) per ciascun intervallo, fornendo il punto finale più grande che è più piccolo del punto iniziale dell'intervallo i-esimo. Utilizzare la ricerca binaria per ottenere nlogn
  3. define d [i] = max (w (i) + d [p (i)], d [i-1]).

inizializza d [0] = 0

Il risultato sarà in d [n] n- il numero di intervalli.

Complessità complessiva 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
Si prega di fornire pseudocodice, piuttosto che richiedere alle persone di comprendere Java (e, in particolare, le raccolte Java).
David Richerby,

2
Ho aggiunto lo pseudo-codice nella prima parte della mia risposta. Volevo solo aggiungere anche il codice Java corrispondente se aiutasse qualcuno a capirlo meglio.
Szilard Mandici,

0

Sì, equivale a un problema con lo zaino. Considera l'ora di fine del lavoro e prepara il tavolo come uno zaino. Prima di leggere la seguente soluzione, controlla il problema dello zaino e la sua soluzione.

// 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

È inoltre possibile stampare i lavori pianificati attraversando la tabella:

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

La complessità è uguale al problema dello zaino.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.