题解 UVA12265 【贩卖土地 Selling Land】
yukimianyan · · 题解
problem
一个黑白矩阵,求以每个点为右下角,能围出的周长最大的全白矩形的周长。
solution
试图进行线性 DP,令
尝试从
换一种思路,我们从一行的角度看它,举个例子:
从点
从这里分出两种理解方式:
代数
将
我们令
已经做完了。
几何
(求以红色方块为右下角的答案,橙色不能踩)
可以把那条绿线看成一种类似轮廓线的东西,我们只需要维护轮廓线底下这些棕色矩形就可以了。容易发现轮廓线是单调不降的,因此我们可以用一个单调栈维护这条线的折角处,顺带维护栈中的最大值(连着折角一起放入栈,一起弹出栈)就可以了。
code
//lock /kk
#include <cstdio>
#include <cstring>
#include <utility>
#include <algorithm>
using namespace std;
int n,m;
char a[5010][5010];
int us[5010][5010],ret[100010];
int stk[5010],mx[5010];
void dp(){
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(a[i][j]=='.'){
us[i][j]=us[i-1][j]+1;
}
}
}
int top=0;
mx[0]=-1e9;
for(int i=1;i<=n;i++){
stk[top=0]=0;
for(int j=1;j<=m;j++){
if(a[i][j]=='#') stk[top=0]=j;
else{
while(top&&us[i][stk[top]]>=us[i][j]) top--;
stk[++top]=j;
mx[top]=max(mx[top-1],us[i][j]-stk[top-1]);
ret[mx[top]+j]++;
}
}
}
}
int mian(){
// freopen("run.in","r",stdin);
// freopen("run.out","w",stdout);
memset(us,0,sizeof us);
memset(ret,0,sizeof ret);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%s",a[i]+1);
dp();
for(int i=1;i<=n+m;i++){
if(ret[i]) printf("%d x %d\n",ret[i],i*2);
}
return 0;
}
int main(){
for(scanf("%*d");~scanf("%d%d",&n,&m);mian());
return 0;
}