最开始,笔者将状态 fif_{i}fi 定义为1到i的最小花费 ,我们不难得到这样的一个状态转移方程,即
fi=(sumti−sumtj+S+Costj)∗(sumfi−sumfj)f_{i}=(sumt_{i}-sumt_{j}+S+Cost_{j})*(sumf_{i}-sumf_{j})fi=(sumti−sumtj+S+Costj)∗(sumfi−sumfj) 。
可是我们发现这时 CostjCost_{j}Costj 非常不好算,而且当前的决策还会对后面的决策产生影响,而且这个转移方程是明显不具备最优子结构的(想一想, 为什么?)。
那么,我们就换一个思路,将 fif_{i}fi 重新定义,我们可将 fif_{i}fi 定义为
fi=min(fj+(sumfi−sumfj)∗(sumti−sumtj+S)+(sumti−sumtj+S)∗(sumfn−sumfi))f_{i}=min(f_{j}+(sumf_{i}-sumf_{j})*(sumt_{i}-sumt_{j}+S)+(sumt_{i}-sumt_{j}+S)*(sumf_{n}-sumf_{i}))fi=min(fj+(sumfi−sumfj)∗(sumti−sumtj+S)+(sumti−sumtj+S)∗(sumfn−sumfi))
即我们定义的 fif_{i}fi 还考虑了对后面的贡献,这样就可以愉快的进行dp了。
时间复杂度是 O(n2)O(n^2)O(n2) ,其实我们还可以用斜率优化将其优化到 O(n)O(n)O(n) ,不过方法不难,笔者就不再阐述。
Code:
#include<cstdio> #include<algorithm> using namespace std; const int maxn = 5002; const long long inf = 10000000000 + 3; long long sumf[maxn], sumt[maxn], f[maxn]; int main() { int n, s; scanf("%d%d",&n,&s); for(int i = 1;i <= n;++i) { scanf("%d%d",&sumt[i], &sumf[i]); sumt[i] += sumt[i - 1], sumf[i] += sumf[i - 1]; } for(int i = 1;i <= n; ++i) { f[i] = inf; for(int j = 0;j < i; ++j) f[i] = min(f[i], f[j] + (sumf[i] - sumf[j]) * (sumt[i] - sumt[j] + s) + (sumt[i] - sumt[j] + s) * (sumf[n] - sumf[i])); } printf("%lld",f[n]); return 0; }