• Tyvj1098任务安排


    这篇讲解部分已经整合进入http://blog.csdn.net/OIljt12138/article/details/51052195 。代码留在这里。

    题目

    描述

    N个任务排成一个序列在一台机器上等待完成(顺序不得改变),这N个任务被分成若干批,每批包含相邻的若干任务。从时刻0开始,这些任务被分批加工,第i个任务单独完成所需的时间是Ti。在每批任务开始前,机器需要启动时间S,而完成这批任务所需的时间是各个任务需要时间的总和(同一批任务将在同一时刻完成)。每个任务的费用是它的完成时刻乘以一个费用系数Fi。请确定一个分组方案,使得总费用最小。
    例如:S=1;T={1,3,4,2,1};F={3,2,3,3,4}。如果分组方案是{1,2}、{3}、{4,5},则完成时间分别为{5,5,10,14,14},费用C={15,10,30,42,56},总费用就是153。

    输入格式

    • 第一行是N(1<=N<=5000)。
    • 第二行是S(0<=S<=50)。
    • 下面N行每行有一对数,分别为Ti和Fi,均为不大于100的正整数,表示第i个任务单独完成所需的时间是Ti及其费用系数Fi。

    输出格式

    • 一个数,最小的总费用。

    测试样例

    • 输入
    5 
    1 
    1 3 
    3 2 
    4 3 
    2 3 
    1 4
    • 输出
    153

    分析

    很显然,由于顺序不能改变,所以可以使用dp来求解。状态是关键!下面给出两个不同的方法,由于状态不同,复杂度相去甚远。
    虽然时间就是金钱,但是这里我们会把F值看作金钱,而时间看成金钱的单位。

    费用S = T * F,显然S = F+F+F...(TF)。
    所以时间T每增加k,即T' = T+k,
               那么S' - S = kF。
    我们便说:金钱又被收了k次。

    统一收取:2D/1D方程

    这是一种最显然的方法,即记录下当前时间,在决策时直接使用Sigma(Time) * F获取当前费用。不妨用dp[i][t]表示从t秒开始,完成任务i到n所需最小费用。不难得出dp方程:

    dp[i][t] = min{dp[k+1][t+T[i..k]]+(t+T[i..k]*F[k])}
    k = i..n
    边界:dp[n+1][..] = 0
    目标:dp[1][0]

    这里一定要理解“统一收取”的意义。事实上,当前使用的时间将会导致未来消费的增加(这显然违反无后效性)。统一收取本质上是增加一维来记录当前时间这个会被改变的状态,从而满足无后效性。
    但是显然这一维的增加是冗余的,而且使时间/空间复杂度达到了无法接受的程度。能不能在每次产生时间改变时(或者形象的,消费时)就计算出在将来会造成的费用呢?

    立刻支付:1D/1D方程

    顺着刚才的思路,我们考虑每当一个决策可能在未来产生消费时,就立刻预先支付这个价值。建议各位先深刻理解刘汝佳/黄亮黑书P151的最优二分检索树是如何推算出dp方程的。
    分析问题: 每个任务的费用是它的完成时刻乘以一个费用系数Fi。不妨看成费用系数Fi进行了连续加法,当前决策之前的每一个决策每使时间过去了1,就要在当前费用中加上一个Fi;反过来,当前的决策每使时间增加k分钟,或者如开头所说收了k次钱,就会使它以及其后的每一个任务的费用加上Fi。正是这种化乘法为加法的思路,削去了冗余的时间一维。由此得出dp方程:

    dp[i]表示第i个到第n个任务所需的费用
    dp[i] = min{dp[j+1] + (T[i..j]+S) * F[i..n]}
    其中 ij ≤ n
    边界是 dp[n+1]=0
    目标是 dp[1]

    深刻理解这个方程,会受益匪浅。

    解决

    方程中可以看出,i是递减的,无脑给出c++代码

    #include <iostream>
    #include <cstring>
    using namespace std;
    
    #define ll int64_t
    ll dp[5005];
    ll sumT[5005],sumF[5005];
    int n,s;
    
    int main() {
        memset(dp,127,sizeof dp);
        cin >> n >> s;
        dp[n+1] = 0;
        int x,y;
        sumT[0] = sumF[0] = 0;
        for (int i=1; i<=n; i++) {
            cin >> x >> y;
            sumT[i] = sumT[i-1]+x;
            sumF[i] = sumF[i-1]+y;
        }
        for (int i=n; i>=1; i--) {
            for (int j=i; j<=n; j++) {
                dp[i] = min(dp[i], dp[j+1]+(s+sumT[j]-sumT[i-1])*(sumF[n]-sumF[i-1]));
            }
        }
        cout << dp[1] << endl;
        return 0;
    }
  • 相关阅读:
    IIS无法显示 XML 页
    asp.net实现sql存取图片
    微软的面试题
    IIS配置.Net问题大全
    Asp.Net调用WebService
    生活本无常,前路更精彩
    【转载】碰到讨厌的老板怎么办
    xxxx不必xx尽,留些xxxx
    【BLOG】Mr梵谷
    机会,从来都是留给有准备的你
  • 原文地址:https://www.cnblogs.com/ljt12138/p/6684393.html
Copyright © 2020-2023  润新知