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);
}