圆环取数

Description

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

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

Input

输入文件的第1行有两个正整数n和k,描述了圆环上的格子数与取数的范围。
第2行有n个正整数,按顺时针方向描述了圆环上每个格子上的数,且指针一开始指向了第1个数字所在的格子。
所有整数之间用一个空格隔开,且不超过10000。

Output

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

Solution

这道题用区间DP,虽然看起来不是特别明显……
我们首先设 f [ l ] [ r ] [ 0 / 1 ] f[l][r][0/1] f[l][r][0/1]表示把 l l l r r r之间的数全部取了,0表示在指针在 l − k − 1 l-k-1 lk1处,1表示指针在 r + k + 1 r+k+1 r+k+1处。
首先我们知道。指针在能取数的时候是必须取数的,否则答案有可能不是最优,我们就可以先把初始位置附近能取的都取了,然后再开始转移。
先把转移式给出来:
f [ l + 1 ] [ r ] [ 0 ] = m i n ( f [ l ] [ r ] [ 0 ] + m x [ l ] [ r ] , m i n ( f [ l ] [ r ] [ 1 ] + m x [ l ] [ r ] ∗ ( n − 2 ∗ k − r + l − 1 ) , f [ l + 1 ] [ r ] [ 0 ] ) ) f[l+1][r][0]=min(f[l][r][0]+mx[l][r],min(f[l][r][1]+mx[l][r]*(n-2*k-r+l-1),f[l+1][r][0])) f[l+1][r][0]=min(f[l][r][0]+mx[l][r],min(f[l][r][1]+mx[l][r](n2kr+l1),f[l+1][r][0]))
f [ l ] [ r − 1 ] [ 1 ] = m i n ( f [ l ] [ r ] [ 1 ] + m x [ l ] [ r ] , m i n ( f [ l ] [ r ] [ 0 ] + m x [ l ] [ r ] ∗ ( n − 2 ∗ k − r + l − 1 ) , f [ l ] [ r − 1 ] [ 1 ] ) ) f[l][r-1][1]=min(f[l][r][1]+mx[l][r],min(f[l][r][0]+mx[l][r]*(n-2*k-r+l-1),f[l][r-1][1])) f[l][r1][1]=min(f[l][r][1]+mx[l][r],min(f[l][r][0]+mx[l][r](n2kr+l1),f[l][r1][1]))
( n − 2 ∗ k − r + l − 1 ) (n-2*k-r+l-1) (n2kr+l1)表示的是从左边走到右边(或从右边走到左边)需要走的步数。
m x [ ] [ ] mx[][] mx[][]是要预处理出来的。
多看几遍,多理解几遍,就能懂了(有什么不懂得可以在下方评论)。

Code

#include<bits/stdc++.h>
#define rg register int
using namespace std;
int n,k,a[2005],mx[2005][2005],f[2005][2005][2],ans=99999999,ans1;
int main()
{
	scanf("%d%d",&n,&k);
	for(rg i=1;i<=n;i++) scanf("%d",&a[i]),ans1+=a[i];
	for(rg i=1;i<=n;i++)
		for(rg j=i;j<=n;j++)
			mx[i][j]=max(mx[i][j-1],a[j]);
	for(rg i=1;i<=n;i++)
		for(rg j=1;j<=n;j++)
			f[i][j][0]=f[i][j][1]=99999999;
	f[k+2][n-k][0]=f[k+2][n-k][1]=0;
	for(rg r=n-k;r>=k+2;r--)
		for(rg l=k+2;l<=r;l++)
		{
			f[l+1][r][0]=min(f[l][r][0]+mx[l][r],min(f[l][r][1]+mx[l][r]*(n-2*k-r+l-1),f[l+1][r][0]));
			f[l][r-1][1]=min(f[l][r][1]+mx[l][r],min(f[l][r][0]+mx[l][r]*(n-2*k-r+l-1),f[l][r-1][1]));
		}
	for(rg i=k+2;i<=n-k+1;i++)
		ans=min(ans,min(f[i][i-1][1],f[i][i-1][0]));
	printf("%d",ans+ans1);
	return 0;
}
  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值