题目链接
题解
写在前面:刘汝佳老师的题解看不懂,自己用暴力搜索(好像也能称为扫描法?)过的
遍历每个空地两次,第一次每行从左到右计算每个空地左边有几个连续相邻的空地;第二次每列从上到下计算每个空地上面有几个连续相邻的空地。
然后最后遍历一次所有空地,以其为右下角找边长最大的矩形。找法:从当前空地(即作为右下角的空地)这列开始,每次计算能构成最大的周长的矩形(即最多能往上几格),然后往左边加上一列,以此类推计算下去直到左边没有连续相邻的空地为止,取最大的周长,这个周长即为这个右下角矩形的最优解。
AC代码
#include <cstdio>
#include <map>
using namespace std;
const int maxn=1000+3;
char temp[maxn][maxn];
struct Cell{
int l,a;
Cell():l(-1),a(-1){}
bool operator < (const Cell& rhs) const {
return l<rhs.l;
}
};
Cell p[maxn][maxn];
map<int,int>ans;
//#define TEST
int main(){
#ifdef TEST
freopen("D:\\college\\clionworkplace\\test_c++2\\in.txt","r",stdin);
freopen("D:\\college\\clionworkplace\\test_c++2\\out.txt","w",stdout);
#endif
int T;
scanf("%d",&T);
while (T--){
int n,m;
ans.clear();
scanf("%d%d",&n,&m);
for (int i = 0; i < n; ++i) {
scanf("%s",temp[i]);
}
// 计算左边的连续空地数
for (int i = 0; i < n; ++i) {
int l=0;
p[i][0].l=0;// 最左边的列的左边没有空地
for (int j = 1; j < m; ++j) {
if(temp[i][j]=='#') continue;
if(temp[i][j-1]=='.') ++l;
else l=0;
p[i][j].l=l;
}
}
// 计算上方的连续空地数
for (int j = 0; j < m; ++j) {
int a=0;
p[0][j].a=0;// 最上面的那一行的上方没有空地
for (int i = 1; i < n; ++i) {
if(temp[i][j]=='#') continue;
if(temp[i-1][j]=='.') ++a;
else a=0;
p[i][j].a=a;
}
}
// 计算并求解每个空地为右下角时的最大矩形周长
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
if(temp[i][j]=='#') continue;
/*
* pre记录当前搜索的列和作为右下角的列之间
* 允许的最大的上方空地数(因为是矩形
* l用来比较周长,这里没有直接计算完整的周长,只算了"高和宽"
* 因为最右下角的空地一定在矩形里,所以每次都没有把他的长宽算进去
* 同理,也没有把同一行的其他列的空地的高算进去,只算上方的空地的高
* 总之就是在比较周长就对了
*/
int l=p[i][j].a,pre=l;
for (int k = 1; k <= p[i][j].l; ++k) {
if(p[i][j-k].a>pre) l=max(l,pre+k);
else{
l=max(l,p[i][j-k].a+k);
pre=p[i][j-k].a;
}
}
l=(l+2)<<1;// 乘二,是周长
if(ans[l]) ++ans[l];// 用map来计数各种周长
else ans[l]=1;// 利用了map如果查找元素不存在就会创建并加入的性质
}
}
for(map<int,int>::iterator it=ans.begin();it!=ans.end();it++){
printf("%d x %d\n",it->second,it->first);
}
}
return 0;
}