vijos1451 区间dp+st表优化

题意

    守护者拿出被划分为n个格子的一个圆环,每个格子上都有一个正整数,并且定义两个格子的距离为两个格子之间的格子数的最小值。环的圆心处固定了一个指针,一开始指向了圆环上的某一个格子,你可以取下指针所指的那个格子里的数以及与这个格子距离不大于k的格子的数,取一个数的代价即这个数的值。指针是可以转动的,每次转动可以将指针由一个格子转向其相邻的格子,且代价为圆环上还剩下的数的最大值。
    现在对于给定的圆环和k,求将所有数取完所有数的最小代价

分析

    很难想的dp,看到环的第一反应还是复制一遍接到后面,但是此题的“取物品”的总消耗是恒定不变的,所以可以贪心的证明,每次把指针所指处所有物品取走是最优解(或者说不能更优),所以一开始指针指向起点时,原来的环就变成了一条链。若指针在起点以右,那么下一步的选择只有再向右走一格或者走到左端点,指针在左时同理。
    由此可得dp转移方程
    设f[i][j][0]表示右端点距起点间隔为i,左端点距起点间隔为j,指针在左时的情况;f[i][j][1]表示右端点距起点间隔为i,左端点距起点间隔为j,指针在右时的情况;
    则:f[i][j][1] = min(f[i][j-1][1] + Add, f[i][j-1][0] + Add*(i+j)) //Add = max{a[2+i+k] .... a[n-j-k+1]}
    f[i][j][0] = min(f[i-1][j][0] + Add, f[i-1][j][1] + Add*(i+j))//Add = max{a[1+i+k] .... a[n-j-k]}
    处理剩余链中最大值时,每次遍历一遍的复杂读太大,故此时用st表或线段树进行维护,由于常数及编码复杂度的原因,此处选择st表。

代码

#include
#include
#include
#include
#include
#define INF 1e15+9
using namespace std;

int n, k;
long long f[2005][2005][2]; //f[i][j][0]表示指向左端点的情况 f[i][j][1]表示指向右端点的情况 
int a[2005], Ma[2005][20];

void make_st() {
    for(int i = 0; i < n; i++)
        Ma[i][0] = a[i];  //区间[i, i]的最小值为i; 
    for(int j = 1; (1<//枚举区间长度
        for(int i = 0; i + (1<1 < n; i++)  //枚举起点,当终点大于n时,停止枚举; 
            Ma[i][j] = max(Ma[i][j-1], Ma[i+(1<<(j-1))][j-1] ); //状态转移 
} //st算法预处理 

long long RMQ(int l,int r)
{
    if (l>r) return 0;
    int k=(int)log2(double(r-l+1));
    return max(Ma[l][k],Ma[r-(1<1][k]);
}

int main() {
    cin >> n >> k;
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    make_st();
    for(int i = 0; i <= n; i++) { //i,j分别表示左/右端点到起点的距离 
        for(int j = 0; j <= n-i; j++) {
            if(i == 0 && j == 0) continue;
            int Add = RMQ(2+i+k, n-j-k+1); //先处理 指向右端点的情况,此时1到((i+1)+k),n到(n-(j-2)-k)已被取完
            if(j > 0) f[i][j][1] = min(f[i][j-1][1] + Add, f[i][j-1][0] + Add*(i+j));
                else f[i][j][1] = INF;
            Add = RMQ(1+i+k, n-j-k);       //同理处理左端点,1到(i+k),n到(n-(j-1)-k)已经取完 
            if(i > 0) f[i][j][0] = min(f[i-1][j][0] + Add, f[i-1][j][1] + Add*(i+j));
                else f[i][j][0] =  INF;
            }
    }
    long long ans = INF;
    for(int i = 0; i <= n; i++) ans = min(ans, min(f[i][n-i][0], f[i][n-i][1]));
    for(int i = 1; i <= n; i++) ans += a[i];
    cout << ans << endl;
    return 0;
}

你可能感兴趣的:(dp,数据结构)