前言
考试打的n^4暴力
想到了可以n^3(其实实现过后是n^2),但时间较少+脑抽,打不出来...
题目
你家刚买了一套新房,想邀请朋友回来庆祝,所以需要一个很大的举行餐桌,餐桌能容纳的人数等于餐桌的周长,你想买一个能容纳最多人的餐桌,餐桌的边必须跟房间的边平行。
给你的房间的设计,计算最多能邀请的客人数。
Input
第一行包含两个整数R和C(1<=R,C<=2000),表示房子的长和宽。
接下来R行每行S个字符(中间没有空格),“.”表示空白区域,“X”表示有障碍物,餐桌所占区域必须是空白的。
Output
输出最多能要求的客人数量。
Sample Input
输入1: 2 2 .. .. 输入2: 4 4 X.XX X..X ..X. ..XX 输入3: 3 3 X.X .X. X.X
Sample Output
输出1: 7 输出2: 9 输出3: 3
【数据规模】
50%的数据R,C<=400
70%的数据R,C<=1000
100%的数据,R,C<=2000
分析
递推预处理出( i , j )这个位置向上的最大高度,接下来两种处理方法:
法一(略暴力,但是易懂又好写):向右扩展,取最小的高度值算周长
法二:单调栈,做法类似“广告印刷”(我自己打了半天,交上去只过了3个点...)
考试暴力30分代码
//n^4暴力
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=2000;
char b[MAXN+5][MAXN+5];
bool a[MAXN+5][MAXN+5];
int pre[MAXN+5][MAXN+5];
int n,m,ans=-10;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%s",b[i]+1);
for(int j=1;j<=m;j++)
if(b[i][j]=='.')
a[i][j]=0;
else
a[i][j]=1;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
pre[i][j]=pre[i-1][j]+pre[i][j-1]-pre[i-1][j-1]+a[i][j];
//枚举左上端点(i,j)
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
//枚举右下端点(k,l)
for(int k=i;k<=n;k++)
for(int l=j;l<=m;l++)
{
int tmp=pre[k][l]-pre[k][j-1]-pre[i-1][l]+pre[i-1][j-1];
if(!tmp)
ans=max(ans,2*((k-i+1)+(l-j+1)));
}
printf("%d",ans-1);//总人数减去主人自己
return 0;
}
/*
根据时限应该n^2 或者 n^2*logn
但是我只会n^4暴力qwq...
*/
法一代码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=2000,INF=0x3f3f3f3f;
char b[MAXN+5][MAXN+5];
bool a[MAXN+5][MAXN+5];
int pre[MAXN+5][MAXN+5];//第i行第j列的最大高度
int n,m,ans=-10;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%s",b[i]+1);
for(int j=1;j<=m;j++)
if(b[i][j]=='.')
a[i][j]=0;
else
a[i][j]=1;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(a[i][j]!=1)
pre[i][j]=pre[i-1][j]+1;
int len,Min;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
if(pre[i][j])
{
len=j;
Min=INF;
while(pre[i][len])
{
Min=min(Min,pre[i][len]);
ans=max(ans,(len-j+1+Min)*2);
len++;
}
}
}
printf("%d",ans-1);//总人数减去主人自己
return 0;
}
法二(没有调对的)代码
#include<cstdio>
#include<stack>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=2000;
char b[MAXN+5][MAXN+5];
bool a[MAXN+5][MAXN+5];
int pre[MAXN+5][MAXN+5];//第i行第j列的最大高度
int n,m,ans=-10;
stack<int> t;
void Solve()
{
int len=0,x;
while(!t.empty())
{
x=t.top();
t.pop();
len++;
ans=max(ans,2*(len+x));
//printf("l:%d x:%d ans:%d\n",len,x,ans);
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%s",b[i]+1);
for(int j=1;j<=m;j++)
if(b[i][j]=='.')
a[i][j]=0;
else
a[i][j]=1;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(a[i][j]!=1)
pre[i][j]=pre[i-1][j]+1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
//if(t.empty()&&pre[i][j])
// t.push(pre[i][j]);
//if(t.empty()&&!pre[i][j])
// continue;
int x;
if(t.empty())
x=1;
else
x=t.top();
//printf("(%d,%d):%d %d------\n",i,j,x,pre[i][j]);
if(pre[i][j]>=x)
t.push(pre[i][j]);
else if(pre[i][j])
{
int len=1;
ans=max(ans,(len+pre[i][j])*2);
//printf("%d\n",pre);
//printf("---ans:%d\n",ans);
while(t.top()>pre[i][j])
{
int x=t.top();
t.pop();
len++;
ans=max(ans,(len+pre[i][j])*2);
//printf("l:%d x:%d pre:%d ans:%d\n",len,x,pre[i][j],ans);
if(t.empty())
break;
}
//if(pre[i][j])
// t.push(pre[i][j]);
}
else
Solve();
//printf("l:%d x:%d ans:%d\n",len,x,ans);
}
Solve();
}
printf("%d",ans-1);//总人数减去主人自己
return 0;
}
/*
试着写一写n^3暴力
f[i][j][k]:左下端点(i,j),宽为k的矩形最大的高
*/
总结
改完题后发现...这道题如果想到法一的打法,应该挺水的...
唉,历练不够啊