Vijos1451圆环取数[环形DP|区间DP]

背景

小K攒足了路费来到了教主所在的宫殿门前,但是当小K要进去的时候,却发现了要与教主守护者进行一个特殊的游戏,只有取到了最大值才能进去Orz教主……

描述

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

现在对于给定的圆环和k,求将所有数取完所有数的最小代价。

格式

输入格式

输入文件cirque.in的第1行有两个正整数n和k,描述了圆环上的格子数与取数的范围。

第2行有n个正整数,按顺时针方向描述了圆环上每个格子上的数,且指针一开始指向了第1个数字所在的格子。

所有整数之间用一个空格隔开,且不超过10000。

输出格式

输出文件cirque.out仅包括1个整数,为取完所有数的最小代价。

样例1

样例输入1[复制]

6 1
4 1 2 3 1 3

样例输出1[复制]

21

限制

对于20%的数据,n≤10,k≤3;
对于40%的数据,n≤100,k≤10;
对于60%的数据,n≤500,k≤20;
对于100%的数据,n≤2000,k≤500;

时限1s。

提示

如上图所示,第一步不转动指针,取走4、3两个数,代价为7;
第2步指针顺时针转动2格,圆环上最大数为3,代价为6,取走1、2、3两个数,代价为6;
第3步指针顺时针转动1格,代价为1,取走剩下的一个数1,代价为1;
最小代价为7+6+6+1+1=21。

--------------------------------------------------

该死该死该死该死该死该死该死该死该死该死该死该死该死该死该死该死该死该死该死该死该死

取数的代价一定,只考虑转移

贪心发现能取就取一定不丢最优解,从1开始,剩下的一定一直是一段区间

然后我就开始做死了f[i][j][0/1]表示剩下[i,j]这段区间,0表示指针在i-k-1处,1表示指针在j+k+1处,调了三四个小时也没调出来

看人家的题解,i和j都来表示左/右“取了几个数”

然而我发现,他们的意思更准确的是左右的指针跳到了哪里

转移如代码,至于哪个>0否则INF,好像是简化了一下情况,不会丢失

该死该死该死

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
typedef long long ll;
const int N=2005,INF=1e9;
int n,k,a[N],f[N][N][2],ans=INF,sum=0;
int mx[N][16];
void initRMQ(int n){
    for(int i=1;i<=n;i++) mx[i][0]=a[i];
    for(int j=1;j<=12;j++)
        for(int i=1;i+(1<<j)-1<=n;i++)
            mx[i][j]=max(mx[i][j-1],mx[i+(1<<(j-1))][j-1]);
}
int RMQ(int l,int r){
    if(l>r) return 0;
    int k=log(r-l+1)/log(2);
    return max(mx[l][k],mx[r-(1<<k)+1][k]);
}
void dp(){
    f[1][n][0]=f[1][n][1]=0;
    for(int i=0;i<=n;i++)
        for(int j=0;j<=n-i;j++){
            if(i==0&&j==0) continue;
            int m=RMQ(i+k+1,n-k-j);
            if(i>0) f[i][j][0]=min(f[i-1][j][0]+m,f[i-1][j][1]+m*(i+j));
            else f[i][j][0]=INF;
            m=RMQ(i+k+2,n-k-j+1);
            if(j>0) f[i][j][1]=min(f[i][j-1][1]+m,f[i][j-1][0]+m*(i+j));
            else f[i][j][1]=INF;
        }
}
int main(int argc, const char * argv[]) {
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),sum+=a[i];
    initRMQ(n);
    dp();
    for(int i=1;i<=n;i++) ans=min(ans,min(f[i][n-i][1],f[i][n-i][0]));
    cout<<ans+sum;
    return 0;
}
时间: 2024-11-06 07:23:43

Vijos1451圆环取数[环形DP|区间DP]的相关文章

Vijos 1451 圆环取数 【区间DP】

背景 小K攒足了路费来到了教主所在的宫殿门前,但是当小K要进去的时候,却发现了要与教主守护者进行一个特殊的游戏,只有取到了最大值才能进去Orz教主-- 描述 守护者拿出被划分为n个格子的一个圆环,每个格子上都有一个正整数,并且定义两个格子的距离为两个格子之间的格子数的最小值.环的圆心处固定了一个指针,一开始指向了圆环上的某一个格子,你可以取下指针所指的那个格子里的数以及与这个格子距离不大于k的格子的数,取一个数的代价即这个数的值.指针是可以转动的,每次转动可以将指针由一个格子转向其相邻的格子,且

HDU 1565 方格取数(1)(状压dp)

感觉这道题目的数据比较水啊,程序的时间复杂度为1711^2*20竟然也可以过掉....其他的就是状压了啊,注意需要滚动一下啊.... 方格取数(1) Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 5701    Accepted Submission(s): 2159 Problem Description 给你一个n*n的格子的棋

hdu 1565 方格取数(1) (状态压缩+dp)

方格取数(1) Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 5589    Accepted Submission(s): 2123 Problem Description 给你一个n*n的格子的棋盘,每个格子里面有一个非负数. 从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出

hdu 2167 方格取数 【状压dp】(经典)

<题目链接> 题目大意: 给出一些数字组成的n*n阶矩阵,这些数字都在[10,99]内,并且这个矩阵的  3<=n<=15,从这个矩阵中随机取出一些数字,在取完某个数字后,该数字周围8个点都不能取,问:取得数字的最大和为多少? 解题分析: 由于对每一个数,有选和不选两种可能,分别对应状态压缩中的1和0,且 n<=15,1<<15不是非常大,因此就可以非常自然的想到状态压缩. 此题要与普通的状压dp不同的是,当某一行取某种方案时,如何求出这种取数的所有取得的数之和,

POJ #3267 The Cow Lexicon 型如&quot; E[j] = opt{D+w(i,j)} &quot;的简单DP 区间DP

Description 问题的描述以及输入输出的样例可以看这里:链接 思路 虽然 DISCUSS 中总有人说水题,但是我觉得这道题的质量可以 (或许我比较弱ORZ ,在做过的 DP 题里算 medium 难度. 题目的意思是给你一个主串和一堆子串,需要你将子串和主串完全匹配上,在匹配过程中可以删除主串中匹配不上的字符,最后统计出被删除的最少字符数目. 比如主串是 carmsr ,子串有 car .mr 两种.可以只用 car 去匹配,那么匹配不上的字符有 m.s.r 三个,所以需要删除三个字符:

Treats for the Cows POJ - 3186 dp 区间dp

//dp[i][j]表示第i次从左边取,第j次从右边取的价值,所以我们可以得到状态方程 //dp[i][j]=max(dp[i-1][j]+(i+j)*a[i],dp[i][j-1]+(i+j)*a[n-j+1]) (i > 0 && j > 0 ) //dp[i][0]=dp[i-1][0]+i*a[i],dp[0][i] dp[0][i-1]+i*a[n-i+1]; #include<cstdio> #include<cmath> #include&

URAL 1776 Anniversary Firework 概率dp+区间dp

A - Anniversary Firework Time Limit:1000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Submit Status Practice URAL 1776 Appoint description:  System Crawler  (2016-05-06) Description Denis has to prepare the Ural State University 90th

codevs 1166 矩阵取数游戏

二次联通门 : codevs 1166 矩阵取数游戏 /* codevs 1166 矩阵取数游戏 SB区间dp dp[l][r] = max (dp[l + 1][r] + number[l], dp[l][r - 1] + number[r]) * 2; 不过要套高精 我用的高精是全部封装好的 可以像平时的int等类型用 缺点就是慢... 慢差不多1/3吧.. */ #include <iostream> #include <cstdio> #include <vector

CSUOJ-1980 不堪重负的数(区间dp)

1980: 不堪重负的树 Submit Page   Summary   Time Limit: 1 Sec     Memory Limit: 128 Mb     Submitted: 57     Solved: 20 Description 小X非常喜欢树,然后他生成了一个大森林给自己玩.玩着玩着,小X陷入了沉思. 一棵树由N个节点组成,编号为i的节点有一个价值Wi. 假设从树根出发前往第i个节点(可能是树根自己),一共需要经过Di个节点(包括起点和终点),那么这个节点对这棵树产生的负担