POJ 1180 斜率DP

题意

要求将一组任务分成多个区块,每个任务都有相应的执行时间。执行每个区块的每个任务的代价为(区块执行完成的最后时间*每个任务的花费)。区块执行完成的最后时间为所有任务执行完成的最后时间+固定值M。问最小花费。

题解

状态转移方程是难点,其他的就是模板了。关于状态转移方程,需要逆序进行推倒。状态转移方程dp[i]=dp[j]+(m+t[i]-t[j])*c[i]。其中t代表逆推的时间和,也就是从N向前累加,即t[i]代表任务i..n的时间和。c代表逆推的和花费和。这个方程的含义是,某一块任务的时间消耗不仅影响了当前的任务块的总消耗,还影响了后面所有任务块的消耗。
推出状态转移方程以后,按照斜率DP的套路解决就可以了。

代码

#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<string>
#include<set>
#include<map>
#include<bitset>
#include<stack>
#include<string>
#define UP(i,l,h) for(int i=l;i<h;i++)
#define DOWN(i,h,l) for(int i=h-1;i>=l;i--)
#define W(a) while(a)
#define MEM(a,b) memset(a,b,sizeof(a))
#define LL long long
#define INF 0x3f3f3f3f
#define MAXN 800010
#define MOD 1000000009
#define EPS 1e-10

using namespace std;
struct Node {
    int t,c;
};
Node nodes[10010];
int t[10010],c[10010],dp[10010],q[10010];
int m;

int getUp(int a,int b) {
    return dp[a]-dp[b];
}
int getDown(int a,int b) {
    return t[a]-t[b];
}
int getDp(int i,int j) {
//    cout<<i<<" "<<j<<" "<<t[i]<<" "<<t[j]<<" "<<dp[j]<<" "<<m<<" "<<nodes[i].c<<endl;
    return dp[j]+(m+t[i]-t[j])*c[i];
}

int main() {
    int n;
    W(~scanf("%d",&n)) {
        MEM(dp,0);
        MEM(t,0);
        MEM(c,0);
        scanf("%d",&m);
        UP(i,1,n+1) {
            scanf("%d%d",&nodes[i].t,&nodes[i].c);
        }
        DOWN(i,n+1,1) {
            t[i]=t[i+1]+nodes[i].t;
            c[i]=c[i+1]+nodes[i].c;
        }
        int st=0,ed=0;
        q[ed++]=n+1;
        DOWN(i,n+1,1) {
            W(st+1<ed&&getUp(q[st],q[st+1])>=c[i]*getDown(q[st],q[st+1])) {
                st++;
            }
            dp[i]=getDp(i,q[st]);
            W(st+1<ed&&getUp(q[ed-1],q[ed-2])*getDown(i,q[ed-1])>=getUp(i,q[ed-1])*getDown(q[ed-1],q[ed-2])) {
                ed--;
            }
            q[ed++]=i;
        }
        printf("%d\n",dp[1]);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值