一道堆的题目
题目描述就是有一n行m列的数字矩阵,现在要求每行找一个数构成一个新的数列,根据排列组合的原理可知,有m^n个数列,需要找出数列加和前n小的数并输出。
数据范围:0<n≤100 ,0<m≤4000, 矩阵中的正整数均不超过10000。
之前有一道堆的比较经典的题目是给出两个长度为n的序列A和B,两个序列中个取一个数相加得到一个新数,有n*n种取法,总共得到了n*n个新数,取前n小的数,当时这题的做法是,把两个序列小到大排序,那么最小的数肯定是A[1]+B[1],第二小的可能是A[1]+B[2]或A[2]+B[1],第三小的可能是之前求第二小时剩下的那个,也有可能是第二小的组合中把A或B的下标向后推一位,那么就有了一个想法,可以用小根堆维护答案,最开始堆中的数为A[1]+B[i](1≤i≤n),每次不停的取最小值并记录,再把取出的A[id]+B[x]转化成A[id+1]+B[x]推进堆里进行比较选择最小值,最后输出就好了。
再回到这题,之前那题可以看成这题的子问题,对于这题,可以行与行之间进行上述处理,每次处理完之后,把堆中取出的前m个作为A,把下一行作为B,重复上述处理操作就可以了。
贴代码↓
type arr=array[0..4005]of longint;
rc=record
w,id:longint;
end;
var heap:array[0..4005]of rc;
a,b:array[0..4005]of longint;
n,m,len:longint;
procedure swap(var x,y:longint);
var t:longint;
begin
t:=x;x:=y;y:=t;
end;
procedure qsort(L,R:longint;var a:arr);
var i,j,mid:longint;
begin
i:=L;j:=R;mid:=a[random(R-L+1)+L];
repeat
while a[i]<mid do inc(i);
while a[j]>mid do dec(j);
if i<=j then begin
swap(a[i],a[j]);
inc(i);dec(j);
end;
until i>j;
if i<R then qsort(i,R,a);
if L<j then qsort(L,j,a);
end;
procedure put(x,id:longint);
var son:longint;
begin
inc(len);
heap[len].w:=x;
heap[len].id:=id;
son:=len;
while (son<>1)and(heap[son].w<heap[son>>1].w) do
begin
swap(heap[son].w,heap[son>>1].w);
swap(heap[son].id,heap[son>>1].id);
son:=son>>1;
end;
end;
function get:rc;
var son,fa:longint;
begin
get:=heap[1];
heap[1]:=heap[len];
dec(len);
fa:=1;
while fa*2<=len do
begin
if (fa*2+1>len)or(heap[fa*2].w<heap[fa*2+1].w) then son:=fa*2
else son:=fa*2+1;
if heap[son].w<heap[fa].w then begin
swap(heap[son].w,heap[fa].w);
swap(heap[son].id,heap[fa].id);
fa:=son;
end
else break;
end;
end;
procedure init;
begin
assign(input,'num.in');reset(input);
assign(output,'num.out');rewrite(output);
readln(n,m);
end;
procedure main;
var i,j:longint;
x:rc;
begin
for i:=1 to m do read(a[i]);readln;
qsort(1,m,a);
for i:=2 to n do
begin
fillchar(heap,sizeof(heap),0);
len:=0;
for j:=1 to m do
begin
read(b[j]);
put(a[1]+b[j],1);
end;
readln;
for j:=1 to m do
begin
x:=get;
b[j]:=x.w;
x.w:=x.w-a[x.id]+a[x.id+1];
put(x.w,x.id+1);
end;
qsort(1,m,b);
a:=b;
end;
end;
procedure print;
var i:longint;
begin
for i:=1 to m do write(a[i],' ');
close(input);close(output);
end;
begin
init;
main;
print;
end.
【写的有漏洞的,欢迎路过大神吐槽】
2016-11-7 23:37:29
Ending.