[区间dp]jzoj1397. 圆环取数

本文介绍了一种基于动态规划的算法,用于解决一个特定的圆环取数游戏问题。玩家需要在一个划分成n个格子的圆环上取走所有正整数,每次操作可以取走指针周围距离小于k的格子内的数值。目标是最小化取数总代价。文章详细解释了如何通过预处理区间最大值和使用动态规划方法来找到最优解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

【问题背景】

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

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

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

Input

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

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

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

Output

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

Sample Input
6 1
4 1 2 3 1 3
Sample Output
21
【样例说明】
    

如上图所示,第一步不转动指针,取走4、3两个数,代价为7;
第2步指针顺时针转动2格,圆环上最大数为3,代价为6,取走1、2、3三个数,代价为6;
第3步指针顺时针转动1格,代价为1,取走剩下的一个数1,代价为1;
最小代价为7+6+6+1+1=21。
【数据规模】
对于20%的数据,n≤10,k≤3;
对于40%的数据,n≤100,k≤10;
对于60%的数据,n≤500,k≤20;

对于100%的数据,n≤2000,k≤500;


反正我这种蒟蒻是没想出来的了。

因为每个数我们都是要取的,所以问题就变成求取完所有数的最小代价。

设f[i][j][0/1]表示向左转i格,向右转j格指针在左(i格)或指针在右(j格)的最小代价。

预处理出区间最大值,t[i][j]表示区间i到j(包括i,j)的最大值。

动态转移方程为 

f[i][j][0]=min(f[i-1][j][0]+mx,f[i-1][j][1]+mx*(i+j));

f[i][j][1]=min(f[i][j-1][1]+mx,f[i][j-1][0]+mx*(i+j));

其中mx表示剩下的最大值。

CODE

#include<cstdio>
#include<cstring>
#include<algorithm>
#define INF 0x3f3f3f
using namespace std;
int n,k,ans,lll;
int a[2000+200],f[2000+20][2000+20][2];
int t[2000+200][2000+200];
int main()
{
	
    //freopen("test.in","r",stdin);
    //freopen("test.out","w",stdout);
    scanf("%d %d" ,&n,&k);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
		lll+=a[i];
	}
	for(int i=1;i<=n;i++)
    {
        int l=i,r=n-1;
        if(r==0)
            r=n;
        t[i][i]=a[i];
        while(l!=r)
        {
            l++;
            if(l>n)
                l=1;
            t[i][l]=max(a[l],t[i][l-1]);
        }
        
    }
    ans=INF;
    printf("%d\n",ans);
    for(int i=0;i<=n;i++)
        for(int j=0;j<=n-i;j++)
        {
            if(!i&&!j)
                continue;
            if(i>0)
            {
                int mx;
				if(i+k+1>n-j-k)
					mx=0;
				else
					mx=t[i+k+1][n-j-k];
                f[i][j][0]=min(f[i-1][j][0]+mx,f[i-1][j][1]+mx*(i+j));
            }
            else
            {
                f[i][j][0]=INF;
            }
            if(j>0)
            {
                int mx;
                if(i+k+2>n-j-k+1)
                	mx=0;
                else
                	mx=t[i+k+2][n-j-k+1];
                f[i][j][1]=min(f[i][j-1][1]+mx,f[i][j-1][0]+mx*(i+j));
            }
            else
            {
                f[i][j][1]=INF;
            }
            if(i+j==n)
            {
                ans=min(ans,f[i][j][1]);
                ans=min(ans,f[i][j][0]);
            }
        }

    printf("%d",ans+lll);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值