(GZ1192)2018.07.06【2018提高组】模拟C组 1.圆环取数【推荐】

圆环取数【推荐】 (Standard IO)

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

Data Constraint

Hint

【样例说明】
  

如上图所示,第一步不转动指针,取走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;

题解:

   本题是区间dp。
   本题要求求出最小代价,总代价=取数代价+转动代价,取数代价=数据总和,所以本题主要是求转动代价。(如果活动范围>=n就,就来个特判,输出数据总和即可╮(╯▽╰)╭)
   一开始指针在1的时候肯定把前后的k个都取了,所以k+2~n-k是未取的数字。
    f[i,j]表示从i到j区间全部取完的最小转动花费,且此时指针指向i-1(f[i,j]和f[j,i]方向不同)转动代价取决于圆环当前数据中的最大值,所以用b[i,j]记录i~j区间中的最大值。在转动过程中取数,将未取的数集中在中间
   状态转移方程分为两个,一个是向原来方向移动一格,另一个是向反移动,二者取最优。

PS:

   如果你取走了一个数,那么这个数会消失,但它所占的位置不变,固各个点之间的距离不变!

var
 a,sum:array[0..2000]of longint;
 f,b:array[0..2000,0..2000]of longint;
 n,m,i,j,k,ans:longint;

function max(a,b:longint):longint;
begin
 if a>b then exit(a);
 exit(b);
end;

function min(a,b:longint):longint;
begin
 if a<b then exit(a);
 exit(b);
end;

begin
 read(n,m);
 for i:=1 to n do
  begin
   read(a[i]);
   sum[i]:=sum[i-1]+a[i];//计算数据总和
   f[i,i]:=a[i];
   b[i,i]:=a[i];
  end;
 if m+m+1>=n then//往前走m,往后走m,能走完圆环,就不需要转动了╮(╯▽╰)╭
  begin
   write(sum[n]);
   halt;
  end;
 for i:=1 to n-1 do//处理出区间和
  for j:=i+1 to n do
   begin
    b[i,j]:=max(b[i,j-1],a[j]);
    b[j,i]:=b[i,j];//可能会往后走,所以需要b[j,i]
   end;
 for k:=1 to n-m-m do//转动距离
  begin
   for i:=m+2 to n-m-k do//往前转,出发的点
    begin
     j:=i+k;//终点
     f[i,j]:=min(f[i+1,j]+b[i,j],f[j-1,i]+((i-m-1)+(n-j-m))*b[i,j]);//一个是向原来方向移动一格,另一个是向反移动,二者取最优。
    end;
   for i:=n-m downto m+2+k do//往后转,出发的点(因为是个环,所以1的后面就是n了)
    begin
     j:=i-k;//终点
     f[i,j]:=min(f[i-1,j]+b[i,j],f[j+1,i]+((j-m-1)+(n-i-m))*b[i,j]);
    end;
  end;
 ans:=max(f[m+2,n-m],f[n-m,m+2])+sum[n];//转动代价+取数代价
 write(ans);
end.
残念~~:

   我崩崩玩了1年多了,还没出圆环之理...

猜你喜欢

转载自blog.csdn.net/SSL_lzx/article/details/80943028
今日推荐