题目链接:http://codeforces.com/problemset/problem/15/D
题意:给定n*m的矩阵,每个点都是一个地基,上面的数字表示该地基的高度。
再给定a*b的小房子,要把a*b放在这个矩阵上,显然建房子要保证选取的地基高度一致。
若不一致,则要把选取的a*b大的矩阵中所有地基都挖低使得和其中一块最矮的地基高度一样,花费是挖的高度和
操作:
选取当前花费最小的一块矩阵,在上面建房子,若有多个方案花费一样,则优先选左上角的。
问:
建的房子的左上角坐标和建该房子的花费
思路:
显然我们要先求出选取每个点时,这个点的地基。用单调队列求一次行的,再在这基础上求一次列的就能得到这个点的地基。
然后把所有点都扔到堆里,跑出解即可。
双端队列的简单用法:
deque<int>q;
q.empty();
q.pop_front(); q.pop_back();
q.push_back(i); q.push_front(i);
单调队列:队尾->队首,队列中存的不是数组的元素,而是数组的下标
单调队列求最小就用递增,对于某一个数要求出这个数u到u+len-1的长度内的最小值:
对于i点,则要求出i点的最小值,我们不能直接求[ i, i+len-1]的值,而是求出[i-len+1, i]的最小值,然后转移到上述的 [u,u+len-1] 上
继续讨论对于i点在单调队列中的求解
1、则队尾元素必须是 [i-len+1,i]区间上的,所以 q.back()>=i-len+1,即->当{ q.back()+len-1<i } 时要把q.back()弹掉
2、要保持单调递增,所以当把这个元素x[i]加入队列时, q.front()必须<=x[i], 所以当 { q.front()>x[i] } 时要把q.front()弹掉
3、此时加入x[i],则此队列的数都在 [i-len+1,i]区间上且元素都是单调递增的,最小的元素是 x[q.back()]
4、显然x[i]的最小值就是 x[q.back()] ,到此计算出x[i]的最小值了
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<set>
#include<vector>
#include<map>
#include<math.h>
#include<queue>
#include<string>
#include<stdlib.h>
#include<algorithm>
using namespace std;
#define N 1015
#define LL __int64
#define ll int
ll x[N], dou[N];
void work(ll len, ll top){
deque<ll>q;// q.front() -> q.back()
for(ll i = 1; i <= top; i++) {
while(!q.empty() && q.front() + len <= i)q.pop_front();
while(!q.empty() && x[i] <= x[q.back()]) q.pop_back();
q.push_back(i);
dou[i] = x[q.front()];
}
}
ll n, m, a, b;
struct node{
ll x,y;
LL val;
node(ll a1=0,ll a2=0,LL a3=0):x(a1),y(a2),val(a3){}
bool operator<(const node& Node) const{
if(Node.val==val)
{
if(Node.x==x) return Node.y<y;
else return Node.x<x;
}
else return Node.val<val;
}
};
priority_queue<node>q;
vector<node>ans;
bool use[N][N];
void go(){
ans.clear();
while(!q.empty()){
node u =q.top(); q.pop();
if(use[u.x][u.y] || use[u.x+a-1][u.y+b-1] || use[u.x][u.y+b-1] || use[u.x+a-1][u.y])continue;
ans.push_back(u);
for(ll i = u.x; i < u.x+a; i++)
for(ll j = u.y; j < u.y+b; j++)
use[i][j] = 1;
}
ll siz = ans.size();
printf("%d\n",siz);
for(ll i = 0; i < siz; i++) printf("%d %d %I64d\n",ans[i].x,ans[i].y,ans[i].val);
}
ll val[N][N], mp[N][N];
LL sum[N][N];
int main(){
ll i,j;
while(~scanf("%d %d %d %d",&n,&m,&a,&b)) {
for(i=1;i<=n;i++)for(j=1;j<=m;j++)
{
scanf("%d",&mp[i][j]);
sum[i][j] = (LL)mp[i][j] + sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
}
for(i=1;i<=n;i++) {
memcpy(x, mp[i], sizeof x);
work(b, m);
for(j=1;j<=m;j++) val[i][j] = dou[j];
}
for(i=b;i<=m;i++) {
for(j=1;j<=n;j++) x[j] = val[j][i];
work(a, n);
for(j=1;j<=n;j++) val[j][i] = dou[j];
}
for(i=a; i<=n; i++)
for(j=b;j<=m;j++)
q.push(node(i-a+1,j-b+1,sum[i][j]-sum[i-a][j]-sum[i][j-b]+sum[i-a][j-b]-(LL)a*(LL)b*(LL)val[i][j]));
go();
}
return 0;
}