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

最开始,笔者将状态 f i f_{i} 定义为1到i的最小花费 ,我们不难得到这样的一个状态转移方程,即

f i = ( s u m t i s u m t j + S + C o s t j ) ( s u m f i s u m f j ) f_{i}=(sumt_{i}-sumt_{j}+S+Cost_{j})*(sumf_{i}-sumf_{j})

可是我们发现这时 C o s t j Cost_{j} 非常不好算,而且当前的决策还会对后面的决策产生影响,而且这个转移方程是明显不具备最优子结构的(想一想, 为什么?)。
那么,我们就换一个思路,将 f i f_{i} 重新定义,我们可将 f i f_{i} 定义为

f i = m i n ( f j + ( s u m f i s u m f j ) ( s u m t i s u m t j + S ) + ( s u m t i s u m t j + S ) ( s u m f n s u m f i ) ) f_{i}=min(f_{j}+(sumf_{i}-sumf_{j})*(sumt_{i}-sumt_{j}+S)+(sumt_{i}-sumt_{j}+S)*(sumf_{n}-sumf_{i}))

即我们定义的 f i f_{i} 还考虑了对后面的贡献,这样就可以愉快的进行dp了。
时间复杂度是 O ( n 2 ) O(n^2) ,其实我们还可以用斜率优化将其优化到 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;
}

猜你喜欢

转载自blog.csdn.net/liyong1009s/article/details/82813149