注意取min dp初始化为INF 乘法用long long
完成时间进行分解 取前缀和形式
j-i 分段延时 则j-n都会延时 dp[0]必须初始化0
#include<bits/stdc++.h>
using namespace std;
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define forn(i,n) for(int i=0;i<n;i++)
#define rep(i,a,n) for(int i=a;i<=n;i++)
typedef long long ll;
const int maxn=5005; //200堆
const int INF=0x3f3f3f3f;
const ll inf=0x3f3f3f3f3f3f3f3f;
int n,s;
ll dp[5005]; //乘法ll 最小花费取min 初始化INF
ll val[maxn],sum[maxn];//5e5
// val前缀和 原先时间的前缀和
int main(){
memset(dp,INF,sizeof dp);
cin>>n>>s;
int t,v;
rep(i,1,n){
cin>>t>>v;
sum[i]=sum[i-1]+t;
val[i]=val[i-1]+v;
}
dp[0]=0;
for(int j=1;j<=n;j++){
for(int i=j;i<=n;i++){ //j-i j=3 i=3
dp[i]=min(dp[i],dp[j-1]+s*(val[n]-val[j-1])+sum[i]*(val[i]-val[j-1])); //1-i分段花费 本次j=3~i=3 上次i肯定到2=j-1
} //sum[i]代表无s延迟的t前缀和
}
cout<<dp[n]<<endl; //将1-n分段的最小花费
return 0;
}
先求出每个任务耗费时间的前缀和 sum 数组,每个任务的权值前缀和 val 数组。
假如从[j,i]进行了一次分段,那么[j,n]的所有任务的完成时间都会往后延 s,这会使得花费答
案增加 s * (val[n] - val[j-1])
而[j,i]中的任务完成时间均为 sum[i],即花费增加了 sum[i] * (val[i] - val[j-1])
也就是说对于每一次分段,我们同时算出了对之后花费增加的贡献和当前完成这一段任务的
花费。因为每次分段都计算了对之后的所有贡献,所以在分段时就不需要考虑之前分段次数
的影响了。(因为之前分段时已经算过贡献)
用 dp[i]表示完成了[1,i]所有任务,最小的总花费。(总花费指已经花费的+对未来花费的贡献)
即 dp[i] = min(dp[i], dp[j] + sum[i] * (val[i] - val[j-1]) + s * (val[n] - val[j-1]))
枚举当前位置 i,和上一次分段的位置 j 即可,复杂度 O(n²)