您的位置:首页 > 其它

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

2011-10-25 16:20 197 查看
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.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: