Encuentre trabajos programados no superpuestos con un costo máximo

8

Dado un conjunto de n trabajos con [hora de inicio, hora de finalización, costo], encuentre un subconjunto para que no se superpongan 2 trabajos y el costo sea máximo.

Ahora no estoy seguro si un algoritmo codicioso hará el truco. Es decir, ordenar por costo y siempre tomar el siguiente trabajo que no se cruza y con un costo máximo entre los dos.

¿Es esto equivalente a un problema de mochila? ¿Cómo podría abordarlo?

usuario1531186
fuente

Respuestas:

8

El gráfico de trabajos superpuestos es un gráfico de intervalo . Los gráficos de intervalo son gráficos perfectos. Entonces, lo que está tratando de hacer es encontrar un conjunto independiente de peso máximo (es decir, no hay dos superposiciones) en un gráfico perfecto . Esto se puede resolver en tiempo polinómico. El algoritmo se da en "Algoritmos polinómicos para gráficos perfectos" , por M. Grötschel, L. Lovász y A. Schrijver.

Hay una serie de casos especiales de gráficos perfectos para los cuales las personas han descubierto algoritmos más simples y más eficientes para esta tarea. No sé si los gráficos de intervalos caen en uno de estos casos especiales.

Peter Shor
fuente
6

El algoritmo codicioso no puede ayudar en ese caso. Y no se puede comparar con problemas de mochila fraccionales o 0-1. El primero podría resolverse mediante un algoritmo codicioso en O (n) y el segundo es NP.

El problema que tiene podría ser la fuerza bruta en O (2 ^ n). Pero podría optimizarlo usando programación dinámica.

1) Ordenar intervalos por hora de inicio.

2) Inicialice int [] cost = new int [jobs.length] con Integer.MIN_VALUE (o cualquier valor negativo);

3) Defina la siguiente rutina recursiva (aquí está 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) Comience la recursión con k = 0;

Solo he implementado la rutina de recursión, mientras que otras partes son triviales. Consideré que cualquier costo es> = 0. Si podría haber trabajos con costos negativos, necesitamos agregar un cheque para eso y simplemente aprobar esos trabajos sin tener en cuenta.

Máxima
fuente
5

Uno podría implementar esto en O (nlogn)

Pasos:

  1. Ordenar los intervalos según la hora de finalización
  2. defina p (i) para cada intervalo, dando el punto final más grande que es más pequeño que el punto inicial del i-ésimo intervalo. Use la búsqueda binaria para obtener nlogn
  3. defina d [i] = max (w (i) + d [p (i)], d [i-1]).

inicializar d [0] = 0

El resultado estará en d [n] n- el número de intervalos.

Complejidad general 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);
      }
    }
  }
}
Szilard Mandici
fuente
2
Proporcione pseudocódigo, en lugar de exigir a las personas que comprendan Java (y, en particular, las colecciones de Java).
David Richerby
2
He agregado el pseudocódigo en la primera parte de mi respuesta. Solo quería agregar el código Java correspondiente también si ayuda a alguien a entenderlo mejor.
Szilard Mandici
0

Sí, es equivalente a un problema de mochila. Considere la hora de finalización del trabajo y prepare la mesa como una mochila. Antes de leer la siguiente solución, consulte el problema de la mochila y su solució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

También puede imprimir los trabajos programados recorriendo la tabla:

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 complejidad es la misma que el problema de la mochila.

Sandesh Kobal
fuente