【堆+区间DP】Orz细菌(orz)

 

Orz细菌(orz)

    话说由于余震的威胁,JDC和全校同学地震当晚只能睡在操场上。JDC睡在操场上,迷迷糊糊就进入了梦想,他做了这样一个梦:

    JW老师最近正在研究一种新型细菌,名为ORZ细菌,这种细菌的生长方式很特别,它们只能通过吞噬同类才能长大(那它们是怎么产生的呢?)。两个orz细菌相遇后,较大的细菌会把较小的细菌吞噬(相同的话就看这两只细菌的RP了),吞噬后较大的细菌的体积会变为两只细菌体积之和,但这个过程会消耗能量,为了方便计算,消耗的能量近似为它们体积之和。

JW老师现在有n只细菌,他每回会从培养皿中取体积为前m小的细菌进行实验,让它们互相吞噬(残忍!)。实验的操作是这样的,JW老师将这m只细菌按体积大小放在一个环形的管道里,再给以细菌刺激,以加快或减慢相邻两只细菌相互吞噬的速度(我们认为这个加速度是无穷大的)。最后把幸存的那只细菌放回培养皿,再进行下次实验。由于细菌吞噬的能量要JW老师来提供,所以他希望经过k次实验后消耗的能量最少。输入数据保证,不会出现细菌不够的情况。

输入数据(orz.in)

  第一行有三个整数,分别为n,m,k

   第二行有n个整数,代表最初n个细菌的体积

   接下来的k行,每行m个整数,第i+2行的第j个数代表第i次实验的第j小的细菌放在哪个位置。例如m=5,第三行为,14235  代表最小的细菌放在第一个位置,第二小的细菌放在第四个位置…最大的细菌放在第五个位置(和第一个位置相邻)

输出数据(orz.out)

  只有一个整数,代表k次实验之后消耗的最小能量。

数据规模

  1<n<=100000,1<=m<=10,1<=k<=10000

   数据保证结果不超过2^31

输入样例

10 2 3

1 2 3 4 5 6 7 8 9 10

1 2

1 2

1 2

输出样例

  18

样例说明(不用输出)

   第一次是用体积为1 2的细菌  最终消耗能量3  变为一个体积为3的细菌

   第二次是用体积为3 3的细菌  最终消耗能量6  变为一个体积为6的细菌

   第三次是用体积为4 5的细菌  最终消耗能量9  变为一个体积为9的细菌

   所以消耗总能量为18

============================

堆+区间DP

=====================

var
  dui:array[1..100000]of longint;
  orz:array[1..10000,1..10]of longint;
  f:array[0..21,0..21]of longint;
  now:array[1..20]of longint;
  sum:array[0..20]of longint;
  dui_s,m,k:longint;
  
procedure init;
begin
  assign(input,'orz.in');
  assign(output,'orz.out');
  reset(input); rewrite(output);
end;

procedure terminate;
begin
  close(input); close(output);
  halt;
end;

procedure shift(r,n:longint);
var
  k:longint;
  tem:longint;
begin
  k:=2*r;
  if (k+1<=n)and(dui[k+1]<dui[k]) then inc(k);
  while (k<=n)and(dui[r]>dui[k]) do
    begin
      tem:=dui[r]; dui[r]:=dui[k]; dui[k]:=tem;
      r:=k;
      k:=2*r;
      if (k+1<=n)and(dui[k+1]<dui[k]) then inc(k);
    end;
end;

procedure adjust(t:longint);
var
  k:longint;
  tem:longint;
begin
  k:=t shr 1;
  while (k>0) and (dui[k]>dui[t])  do
    begin
      tem:=dui[k];
      dui[k]:=dui[t];
      dui[t]:=tem;
      t:=k;
      k:=t shr 1;
    end;

end;

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

procedure main;
var
  i,j:longint;
  tot,ans:longint;
  len,h,k,t:longint;
begin
  readln(dui_s,m,k);
  
  for i:=1 to dui_s do read(dui[i]);
  for i:=dui_s shr 1 downto 1 do shift(i,dui_s);
  for i:=1 to k do
    for j:=1 to m do
      read(orz[i,j]);
      
  ans:=0;
  for i:=1 to k do
   begin
     t:=0;
     for j:=1 to m do
       begin
         t:=t+dui[1];
         now[orz[i,j]]:=dui[1];
         dui[1]:=dui[dui_s];
         dec(dui_s);
         shift(1,dui_s);
       end;               //弹出前m小得元素..
     inc(dui_s);
     dui[dui_s]:=t;
     adjust(dui_s);
   //for j:=dui_s shr 1 downto 1 do
   //shift(j,dui_s);       //新细胞插入堆.向上调整。
       
     for j:=1 to m do now[j+m]:=now[j];
     //记录每个位置状态..
     
     fillchar(f,sizeof(f),$7);
     for j:=1 to 2*m do
       begin
         f[j,j]:=0;
         sum[j]:=sum[j-1]+now[j];
       end;

     
     for len:=2 to m do     //枚举区间长度..
       for h:=1 to 2*m-len do     //枚举头指针
         begin
           t:=h+len-1;        //算出尾指针
           for k:=h to t-1 do   //枚举断点..
             begin
               f[h,t]:=min(f[h,t],f[h,k]+f[k+1,t]+sum[t]-sum[h-1]);
             end;
         end;
         
     tot:=maxlongint;
     for j:=1 to m do
       if tot>f[j,j+m-1] then tot:=f[j,j+m-1];
     //找出将这个区间合并代价最少的.
       
     ans:=ans+tot;

   end;
  writeln(ans);
end;

begin
  init;
  main;
  terminate;
end.       

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值