• 【BZOJ】P2448 挖油


    区间dp+决策单调性

    题目链接

    part1.朴素dp

    (dp[l][r])表示l到r这个区间最坏情况下的最小花费。

    那么考虑在([l,r])中任选一个点k挖,此时有两种情况:

    1.k点有油,那么后续代价就是(dp[k+1][r])

    2.k点没有油,那么后续代价就是(dp[l][k-1])

    显然,为了使情况最坏,我们要在(dp[k+1][r])(dp[l][k-1])中取最大值。

    要使最坏情况下花费最小,我们要在所有k中取最小值。

    那么,转移方程就是:

    [dp[l][r]=min{max{dp[l][k-1],dp[k+1][r] }+A[k]} kin[l+1,r-1] ]

    其中(A[i])表示在点i勘测是否有油的花费。

    这样,就有一个(O(n^3))朴素dp了,期望得分30分。


    part2.决策单调性优化

    我们要的是([l,r])的最小值,所以应该是维护单调递增的队列。

    但还有一个问题,转移方程中有(max{dp[l][k-1],dp[k+1][r] }),我们要想办法把这个max消掉。

    那如何消去max呢?

    我们维护一个k,使得(dp[l][k-1])刚好大于(dp[k+1][r])或者(dp[k+1][r])刚好大于(dp[l][k-1])

    现在,我们只需证明k是具有单调性的就可以了。

    首先,不难发现这样一个性质:对于任意的l,r,都有(dp[l][r]le dp[l][r+1])

    我们以维护(dp[l][k-1])刚好大于(dp[k+1][r])为例。

    那么,假设l,k固定,随着r的不断增大,(dp[k+1][r])也会不断增大,而(dp[l][k-1])不变,因此k肯定会不断向右移

    同理,维护(dp[k+1][r])刚好大于(dp[l][k-1])时,k肯定也会不断向左移

    于是,我们在维护最小值的同时再不断弹出队头不合法的元素,这样就保证转移一定是从(dp[l][k-1])或者(dp[k+1][r])转移过来的。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    int n,A[2010],dp[2010][2010],Q[2010][2010],head[2010],tail[2010];
    //要以每个点都作为l和r的情况各开一个单调队列
    //但是由于dp中l是第一层循环,那么枚举一个l后就不会在遍历到了,因此可以把所有点作为l的情况公用一个单调队列
    //那么开n+1个单调队列就可以了
    int calcl(int l,int r,int k){
        return dp[l][k-1]+A[k];
    }
    int calcr(int l,int r,int k){
        return dp[k+1][r]+A[k];
    }
    int main(){
        memset(dp,63,sizeof(dp));
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&A[i]);
        for(int i=1;i<=n;i++)head[i]=1;
        for(int l=n;l>=1;l--){
            head[0]=1;
            tail[0]=0;
            dp[l][l]=A[l];
            Q[0][++tail[0]]=l;
            for(int r=l+1;r<=n;r++){
                while(head[0]<=tail[0]&&calcl(l,r,Q[0][head[0]])<calcr(l,r,Q[0][head[0]]))head[0]++;
                while(head[0]<=tail[0]&&calcl(l,r,r)<calcl(l,r,Q[0][tail[0]]))tail[0]--;
                Q[0][++tail[0]]=r;
                while(head[r]<=tail[r]&&calcr(l,r,Q[r][head[r]])<calcl(l,r,Q[r][head[r]]))head[r]++;
                while(head[r]<=tail[r]&&calcr(l,r,l)<calcr(l,r,Q[r][tail[r]]))tail[r]--;
                Q[r][++tail[r]]=l;
                dp[l][r]=min(calcl(l,r,Q[0][head[0]]),calcr(l,r,Q[r][head[r]]));
            }
        }
        cout<<dp[1][n];
        return 0;
    }
    
  • 相关阅读:
    2017-2018-2 20165207 实验四《Android开发基础》实验报告
    2017-2018-2 20165207 实验三《敏捷开发与XP实践》实验报告
    20165207 第九周学习总结
    20165328 实验四《Andriid应用开发》实验报告
    20165328 第十二周课上补做
    20165328 课下作业补做
    20165328 第九周学习总结
    2017-2018-2 20165328 实验三《敏捷开发与XP实践》实验报告
    20165328 结对编程第二周整体总结
    20165328课上补做
  • 原文地址:https://www.cnblogs.com/SillyTieT/p/11636304.html
Copyright © 2020-2023  润新知