题意
要求将一组任务分成多个区块,每个任务都有相应的执行时间。执行每个区块的每个任务的代价为(区块执行完成的最后时间*每个任务的花费)。区块执行完成的最后时间为所有任务执行完成的最后时间+固定值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]);
}
}