习题:建房子(模拟+单调队列)
tyvj1750建房子
描述
小D终于成为了一位建筑师。他的第一个任务是在一块n行m列的矩形土地上砌房子,每个房子的大小是a行b列(不能旋转)。
矩形土地的每个单元格都有一个高度。如果选定某个区域上为建房子的地方,那么需要将这个区域的每个单元格的高度变成这个区域的最小的单元格的高度,因为这样能使土地更平整。将一个单元格的高度从h2变为h1所花费的代价是h2-h1,一个区域所花费的代价为其每个单元格所花费的代价之和。
现在小D按下面所述的方式建房子:
1、首先找到矩形土地中花费最少代价就能建房子的区域(这个区域中不能有某个单元格已经砌了房子),如果有多个这样的区域,选择左上角所在行尽可能小的,如果行相同,选择列尽可能小的。
2、接下来在这块区域砌一栋房子。
3、重复上述操作,直到找不到一个可以砌房子的区域。
现在需要你告诉小D,他一共将砌多少栋房子,每栋房子的左上角的坐标,以及每栋房子所花费的代价。
格式
输入格式
输入的第一行包括四个正整数
n,m,a,b(含义如上所述)
接下来n行,每行m个非负整数
其中第i行,第j列表示第i行,第j列的单元格的高度。
输出格式
输出第一行为一个非负整数S:表示一共砌了多少栋房子
接下来S行
每行三个整数i,j,v:表示第S个房子的左上角的所在行,所在列,以及所花费的代价。
样例1
样例输入1
3 2 1 2
2 5
1 2
3 5
样例输出1
3
2 1 1
3 1 2
1 1 3
样例2
样例输入2
5 5 2 2
9 9 4 4 10
10 3 3 9 3
3 6 3 8 1
4 2 10 9 8
7 10 10 3 5
样例输出2
4
2 2 3
4 4 13
1 4 14
4 1 15
限制
每个测试点2s
提示
30%保证:n <= 10, m <= 10
70%保证:n <= 300, m <= 300
100%保证:n <= 1000, m <= 1000
1<= a <= n, 1 <= b <= m, 每个单元格的高度 <= 10^9
分析:
首先要在n*m时间内预处理出大矩形内各小矩形数值和与最小值,
利用递推可求出和,利用二次单调队列求出最小值。
然后和-min*a*b即为个小矩形对应花费,存在相应左上角。
这样我们把信息存在了(n-a+1)(m-b+1)的数组内,每个点对应以该点为左上角的矩形花费。
然后排序,依次取最小值,删除会影响的点,设为false即可。显然时间是O(n*m)级别的。
本题让我意识到int64有多慢,排序时如果在交换坐标值时中介变量用int64会超时,用longint瞬间过了。
代码:
program house; var mk:array[-1001..1001,-1001..1001]of boolean; f,g,w,p,v:array[-1..1001,-1..1001]of int64; q:array[0..2000]of longint; lx,ly,answer:array[0..1000000]of longint; c:array[0..1000000]of int64; n,m,a,b,t:int64; i,j,x,y,ans:longint; procedure qsort(l,h:longint); var i,j:longint; m,t,m1,m2:int64;t1:longint; begin i:=l; j:=h; m:=c[(i+j) div 2]; m1:=lx[(i+j) div 2]; m2:=ly[(i+j) div 2]; repeat while (c[i]<m)or((c[i]=m)and(lx[i]<m1))or((c[i]=m)and(lx[i]=m1)and(ly[i]<m2)) do inc(i); while (m<c[j])or((c[j]=m)and(m1<lx[j]))or((c[j]=m)and(lx[j]=m1)and(m2<ly[j])) do dec(j); if i<=j then begin t:=c[i]; c[i]:=c[j]; c[j]:=t; t1:=lx[i]; lx[i]:=lx[j]; lx[j]:=t1; t1:=ly[i]; ly[i]:=ly[j]; ly[j]:=t1; inc(i); dec(j); end; until i>j; if i<h then qsort(i,h); if j>l then qsort(l,j); end; procedure work1(x:longint); var i,h,t:longint; begin h:=1; t:=0; q[1]:=0; for i:=1 to m do begin while (h<=t)and(q[h]+b-1<i) do inc(h); while (h<=t)and(w[x,i]<=w[x,q[t]]) do dec(t); inc(t); q[t]:=i; if i>=b then p[x,i-b+1]:=w[x,q[h]]; end; end; procedure work2(x:longint); var i,h,t:longint; begin h:=1; t:=0; q[1]:=0; for i:=1 to n do begin while (h<=t)and(q[h]+a-1<i) do inc(h); while (h<=t)and(p[i,x]<=p[q[t],x]) do dec(t); inc(t); q[t]:=i; if i>=a then v[i-a+1,x]:=p[q[h],x]; end; end; begin readln(n,m,a,b); for i:=1 to n do begin for j:=1 to m do read(w[i,j]); readln; end; for i:=1 to n do for j:=1 to m do f[i,j]:=f[i-1,j]+f[i,j-1]-f[i-1,j-1]+w[i,j]; for i:=1 to n-a+1 do for j:=1 to m-b+1 do g[i,j]:=f[i+a-1,j+b-1]-f[i-1,j+b-1]-f[i+a-1,j-1]+f[i-1,j-1]; for i:=1 to n do work1(i); for i:=1 to m-b+1 do work2(i); t:=0; for i:=1 to n-a+1 do for j:=1 to m-b+1 do begin g[i,j]:=g[i,j]-a*b*v[i,j]; inc(t); c[t]:=g[i,j]; lx[t]:=i; ly[t]:=j; end; qsort(1,t); for i:=1 to n do for j:=1 to m do mk[i,j]:=true; for i:=1 to t do if mk[lx[i],ly[i]]=true then begin for x:=lx[i]-a+1 to lx[i]+a-1 do for y:=ly[i]-b+1 to ly[i]+b-1 do mk[x,y]:=false; inc(ans); answer[ans]:=i; end; writeln(ans); for i:=1 to ans do writeln(lx[answer[i]],' ',ly[answer[i]],' ',g[lx[answer[i]],ly[answer[i]]]); end.