带权的区间调度:动态规划

15 篇文章 0 订阅

带权的区间调度

存在单一资源R,有n个需求{1, 2, …, n},每个需求指定一个开始时间bi与一个结束时间ei,在时间区间[bi, ei]内该需求想要占用资源R,资源R一旦被占用则无法被其他需求利用。每个需求i带有一个权值vi,代表该需求被满足之后能够带来的价值或者贡献。如果两个需求的时间区间不重叠,那么它们是相容的。带权值的区间调度问题即,对上面所描述的情境,求出一组相容子集S使得S中的区间权值之和最大。

解决此类问题一般采用动态规划的思路,执行步骤分以下几个阶段:

  • 对所有的需求按照结束时间排序。
  • 求出所有需求前面所有的需求中和它相容的最后一个需求,记为pi。
  • 若只在前i个需求中求相容子集,则可以得到的最大权值之和为M[i],可以推导出:M[i]=max(wi+M[pi],M[i-1]),即如果最大的相容子集中包含需求i,则其权值之和为需求i的权值加上之前所有和它相容的需求子集的最大相容子集的权值之和;若不包含需求i,则其权值之和为前i-1个需求而的最大相容子集的权值之和。而应在其中取最大值,作为M[i]。
  • 计算出来所有的M[i]之后,自顶而上,求出所有使权值之和变为最大的需求。

源代码

测试数据如下:

任务开始时间结束时间重要程度
0042
1164
2574
3297
48102
58111

测试输出如下:

(  8, 10,  2)
(  5,  7,  4)
(  0,  4,  2)

Task类

import java.util.ArrayList;
import java.util.Collections;

class Task implements Comparable<Task> {
    int begin;
    int end;
    int weight;
    int p;

    public Task(int begin, int end, int weight) {
        this.begin = begin;
        this.end = end;
        this.weight = weight;
        this.p = -1;
    }

    public static void computeP(ArrayList<Task> tasks) {
        Collections.sort(tasks);
        for (int i = 0; i < tasks.size(); i++) {
            for (int j = 0; j < tasks.size(); j++) {
                if (tasks.get(j).begin >= tasks.get(i).end) {
                    tasks.get(j).p = i;
                }
            }
        }
    }

    @Override
    public int compareTo(Task o) {
        return new Integer(end).compareTo(new Integer(o.end));
    }

    @Override
    public String toString() {
        return String.format("(%3d,%3d,%3d)", begin, end, weight);
    }
}

WeightedIntervalScheduling类

import java.util.ArrayList;

public class WeightedIntervalScheduling {
    private ArrayList<Task> tasks = new ArrayList<>();

    private void initTasks() {
        tasks.add(new Task(0, 4, 2));
        tasks.add(new Task(1, 6, 4));
        tasks.add(new Task(5, 7, 4));
        tasks.add(new Task(2, 9, 7));
        tasks.add(new Task(8, 10, 2));
        tasks.add(new Task(8, 11, 1));
        Task.computeP(tasks);
    }

    private int[] M;

    private void initM() {
        M = new int[tasks.size()];
        for (int i = 0; i < M.length; i++) {
            M[i] = -1;
        }
    }

    public void init() {
        initTasks();
        initM();
    }

    private int M_Compute_Opt(int i) {
        if (i < 0) {
            return 0;
        }
        if (M[i] == -1) {
            M[i] = Math.max(tasks.get(i).weight + M_Compute_Opt(tasks.get(i).p), 
            M_Compute_Opt(i - 1));
        }
        return M[i];
    }

    private void Find_Solution(int i) {
        if (i < 0) {} else if (tasks.get(i).weight + M_Compute_Opt(tasks.get(i).p) >
         M_Compute_Opt(i - 1)) {
            System.out.println(tasks.get(i));
            Find_Solution(tasks.get(i).p);
        } else {
            Find_Solution(i - 1);
        }
    }

    public void Find_Solution() {
        Find_Solution(tasks.size() - 1);
    }

    public static void main(String[] args) {
        WeightedIntervalScheduling wis = new WeightedIntervalScheduling();
        wis.init();
        wis.Find_Solution();
    }
}
  • 1
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值