题意:要在一片n*m的地上盖一个a*b的房子。这片地参差不齐,如果选定一个a*b的区域盖房子的话,需要把这片地铲地和最低点一样平,消耗的代价为铲掉高度之和。按代价大小求所有不重叠的房子,如果代价相同,靠上的优先;如果在同一行,靠左的优先。
解:枚举每个点,在以这个点为右下角盖房子,消耗的代价为这片区域高度之和减去最低点乘以区域面积。高度之和可以用二位前缀和维护,区域最低点可以跑两遍单调队列O(mn)地求出。然后排序,判断一下有没有重叠,输出。
代码:
#include <bits/stdc++.h> using namespace std; #define maxx 1005 #define maxn 25 #define maxm 205 #define ll long long #define inf 1000000009 #define mod 2520 struct node{ ll x,y,w; bool operator < (const node &a) const{ if(w==a.w) { if (x == a.x) { return y < a.y; } return x<a.x; } return w<a.w; } node(ll xx,ll yy,ll ww){ x=xx;y=yy;w=ww; } }; ll n,m,a,b; ll c[maxx][maxx]; ll row[maxx][maxx]={0},col[maxx][maxx]={0}; ll sum[maxx][maxx]={0}; int vis[maxx][maxx]={0}; int q[maxx]={0}; vector<node> v; ll cal(int x,int y){ return sum[x][y]-sum[x-a][y]-sum[x][y-b]+sum[x-a][y-b]; } signed main() { scanf("%I64d%I64d%I64d%I64d",&n,&m,&a,&b); for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ scanf("%I64d",&c[i][j]); } } for(int i=1;i<=n;i++){ int head=1,tail=0; for(int j=1;j<=m;j++){ while(head<=tail&&q[head]<j-b+1) head++; while(head<=tail&&c[i][q[tail]]>=c[i][j]) tail--; q[++tail]=j; row[i][j]=c[i][q[head]]; } } for(int j=1;j<=m;j++){ int head=1,tail=0; for(int i=1;i<=n;i++){ while(head<=tail&&q[head]<i-a+1) head++; while(head<=tail&&row[q[tail]][j]>=row[i][j]) tail--; q[++tail]=i; col[i][j]=row[q[head]][j]; } } // for(int i=1;i<=n;i++){ // for(int j=1;j<=m;j++){ // printf("%d ",row[i][j]); // } // printf("\n"); // } for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+c[i][j]; } } for(int i=a;i<=n;i++){ for(int j=b;j<=m;j++){ v.push_back(node(i,j,cal(i,j)-a*b*col[i][j])); } } sort(v.begin(),v.end()); vector<node> ans; for(auto [x,y,z]:v){ if(vis[x][y]||vis[x-a+1][y]||vis[x][y-b+1]||vis[x-a+1][y-b+1]) continue; ans.push_back(node(x-a+1,y-b+1,z)); for(int i=x-a+1;i<=x;i++){ for(int j=y-b+1;j<=y;j++){ vis[i][j]=1; } } } printf("%d\n",ans.size()); for(auto [x,y,z]:ans){ printf("%I64d %I64d %I64d\n",x,y,z); } return 0; } //dp[i][j]