洛谷P2365任务分配(斜率优化+思维+DP)

9 篇文章 0 订阅
1 篇文章 0 订阅

P2365任务分配(斜率优化+思维+DP)

题意:

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。

题解:斜率优化+思维+DP

A:开始的时候我直接上来拍了一个二维DP[I][J],代表前I个分成J段的花费,然后状态转移方程
DP[I][J] = MIN(DP[I][J], DP[K][J-1] + (SJ+T[I])(C[I]-C[J]),K代表前K个,C和T分别代表了花费和时间的前缀和,这个式子很好推,但是会T飞起来。也不好优化。O(N^3)。
B:后来一想,这个没有要求分成几段,也就是可以把J给压缩掉,用DP[I]来表示前I个可以达到的最小费用。状态方程变成
DP[I] = MIN(DP[I], DP[J] + T[I] * (C[I]-C[J]) + S*[C[N] - C[J]])。这里和上面不同的就是S*[C[N] - C[J]],这里用了一个逆向思维,因为前面有几段没有办法计算,我们就去想后面的会被怎么影响,那么后面的S的影响就是当前的S乘上后面所有的费用。这样就OK了。即:费用提前的思想。注意要预处理了一下,I之前所有的物品分成一段的情况。然后这题目O(N^2)就可以过了,但是某某大佬突然对我这个蒟蒻说了一句,这题O(N)就可以过,于是就有了第三种解法。
C:斜率优化。
首先我们先化简B的状态转移方程:DP[I] = MIN(DP[I], DP[J] + T[I] * (C[I]-C[J]) + S*[C[N] - C[J]])。化简得到:
DP[J] = C[J](T[I]+S)+DP[I]-T[I]C[I]-SC[N]; 把这个方程想象为f(x) = kx+b。那么f(x)就是DP[J]。k是(T[I]+S)。x是C[J],b就是DP[I]-T[I]C[I]-SC[N]。那么这样的方程就可画出一条折线来,因为我们要DP[I]最小,那就是b最小,所以我们要找所有折点中能让b最小的点。这里可以用手动模拟一下。
在这里插入图片描述
可以想象一下以上图片。由图片我们可以知道:会让目标线B最小的肯在J2,而J2的斜率也是正好在大雨目标线的第一个斜率。即下凸的最优解。当然也有不能的情况,比如以下:
在这里插入图片描述
在这里中J2就是不满足的,这里J1的斜率就认为还是小于目标线的,但这样上凸的情况我们要舍弃,所以我们直接吧J2点忽略,链接J1和J3,以此类推其他情况。最后我们用一个优先队列来维护折点,把凸包中小于的都舍弃掉,直到最优解,后面的上凸的情况就直接把大于的删除,然后把J3压进去。然后就是方程直接写,一直递推。

代码B:O(N^2)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mx = 3e5 + 7;
const ll INF = 8e9 + 7;
struct node {
    ll a, b;
} z[mx];
ll pret[mx],prec[mx];
ll dp[mx];
int main() {
    ios_base::sync_with_stdio(false);
#ifdef ACM_LOCAL
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
#endif
    int s,n;
    cin>>n>>s;
    pret[0] = prec[0] = 0;
    for (int i=1;i<=n;i++) {
        cin >> z[i].a >> z[i].b;
        pret[i] += pret[i-1]+z[i].a;
        prec[i] += prec[i-1]+z[i].b;
        dp[i] = INF;
    }
    for (int i=1;i<=n;i++)
        dp[i] = pret[i] * (prec[i]) + s * (prec[n]);  //考虑只有N个元素只分一段的情况
    for (int i=1;i<=n;i++) {
        for (int j = 1; j < i; j++)
            dp[i] = min(dp[i], dp[j] + pret[i] * (prec[i] - prec[j]) + s * (prec[n] - prec[j]));
    }
    cout<<dp[n]<<endl;
    return 0;
}

代码C:O(N)斜率优化

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mx = 3e5 + 7;
const ll INF = 8e9 + 7;
struct node {
    ll a, b;
} z[mx];
ll pret[mx],prec[mx];
ll dp[mx];
ll q[mx];
int main() {
    ios_base::sync_with_stdio(false);
#ifdef ACM_LOCAL
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
#endif
    int s,n;
    cin>>n>>s;
    pret[0] = prec[0] = 0;
    for (int i=1;i<=n;i++) {
        cin >> z[i].a >> z[i].b;
        pret[i] += pret[i-1]+z[i].a;
        prec[i] += prec[i-1]+z[i].b;
        dp[i] = INF*INF;
    }
    dp[0] = 0;
    int l =1,r = 1;
    for (int i=1;i<=n;i++){
        while (l<r && (dp[q[l+1]]-dp[q[l]])<=(s+pret[i])*(prec[q[l+1]]-prec[q[l]])) l++;
        dp[i] = dp[q[l]]+pret[i]*prec[i] + s*prec[n] - prec[q[l]]*(s+pret[i]);
        while (l<r && (dp[i] - dp[q[r]])*(prec[q[r]]-prec[q[r-1]])<= (dp[q[r]] -dp[q[r-1]])*(prec[i] - prec[q[r]])) r--;
        q[++r] = i;
    }
    cout<<dp[n]<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值