洛谷 P2365 任务安排_代价提前计算 + 好题

最开始,笔者将状态 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=(sumtisumtj+S+Costj)(sumfisumfj)

可是我们发现这时 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+(sumfisumfj)(sumtisumtj+S)+(sumtisumtj+S)(sumfnsumfi))

即我们定义的 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;
}

  

原文地址:https://www.cnblogs.com/guangheli/p/9845155.html