思路:
读入的时候先处理一下,使得 map[i][j] 记录的是 第 i 行中 连续到第 j 列有多少个 F.
就样例而言
R F F F F F
F F F F F F
R R R F F F
F F F F F F
F F F F F F
可以被处理为
0 1 2 3 4 5
1 2 3 4 5 6
0 0 0 1 2 3
1 2 3 4 5 6
1 2 3 4 5 6
对于任意一个 map[i][j],我们如果要找到包含该点在内的最大的面积的话,只要关注这一列的状况,因为 map[i][j] 本身就记录了该行的状况。
从 第i行 开始向上找,map[i-k][j] 的最小值就代表了从 第j列开始可以向左拓展的最大宽度(类似木桶原理(?),再乘上向上找的行数,就是一块矩形的面积了,然后所有这些面积里面取最大值,就是答案。
另外考虑到可能有很多0,那么我们向上一旦找到一个0,就可以停了,显然以后的最小值都会是0,乘出来也都会是0。从图形上理解,这代表着从 map[i-k][j] 那点开始是不能够连续到该点的,更不用说向上了。
以样例的最后一行(i=6)为例
j=1时,1*1,1*2 最大值为2
j=2时,2*1,2*2 最大值为4
j=3时,3*1,3*2 最大值为6
j=4时,4*1,4*2,1*3,1*4,1*5 最大值为8
j=5时,5*1,5*2,2*3,2*4,2*5 最大值为10
j=6时,6*1,6*2,3*3,3*4,3*5 最大值为15
(上面的式子都是宽度乘高度)
后来看discuss的时候看到一种很棒的做法,后面再讲。
(其实这段代码我写的一点也不像dp)
AC代码如下:
//1505.cpp
#include <iostream>
#include <string>
#define maxn 1010
int map[maxn][maxn],minim[maxn][maxn];
using namespace std;
int main()
{
int T,m,n,i,j,ans,k,x,anss;
string s;
char ch;
cin >> T;
while (T--)
{
cin >> m >> n;
for (i=1;i<=m;++i)
for (j=1;j<=n;++j)
{
cin >> ch;
if (ch=='F')
map[i][j] = map[i][j-1]+1;
else
map[i][j] = 0;
}
anss = 0;
for (i=1;i<=m;++i)
for (j=1;j<=n;++j)
{
x = ans = map[i][j];
for (k=1;k<=i-1;++k)
{
if (map[i-k][j]==0)
break;
x = min(x,map[i-k][j]);
ans = max(ans,x*(k+1));
}
anss = max(anss,ans);
}
cout << anss*3 << endl;
getline(cin,s);
}
return 0;
}
discuss里面有一种做法,点击打开链接,是先预处理好l,r,h数组。
其中一个很棒的想法是,对于l数组,每次和左边比较的时候,不需要一个一个比过去,因为如果 h[i-1]>=h[i] 就这样去判断 l[i-1] 所指的位置的前一个位置是不是满足这个条件然后一直往前找,这样大大减少了循环次数。
1506
其实和1505一样的道理,只是换成了一维的
但是要注意 long long
看discuss里面学到了很棒的几个做法
一个是分治,用了栈,点击打开链接
另一个是单调栈做法,感觉学习到了很多,单调栈相关
分治AC代码如下:
//1506.cpp
#include <iostream>
#include <stack>
#define maxn 100010
long long a[maxn];
using namespace std;
long long maxi(long long l,long long r);
int main()
{
// freopen("1506.in","r",stdin);
// freopen("1506.out","w",stdout);
long long n,i;
while (cin >> n && n)
{
for (i=0;i<n;++i)
cin >> a[i];
cout << maxi(0,n-1) << endl;
}
// fclose(stdin);
// fclose(stdout);
return 0;
}
long long maxi(long long l,long long r)
{
if (r-l==1)
return max(max(a[r],a[l]),min(a[r],a[l])*2);
if (r==l)
return a[l];
// cout << l << " " << r << endl;
stack<long long> left,right;
long long i,j,low,len,ans;
long long mid = (l+r) >> 1;
long long maxl = maxi(l,mid);
// cout << l << " " << r << " " << maxl << endl;
long long maxr = maxi(mid+1,r);
// cout << l << " " << r << " " << maxr << endl;
for (i=l;i<=mid;++i)
left.push(a[i]);
for (i=r;i>mid;--i)
right.push(a[i]);
low = INT_MAX;
len = ans = 0;
while (!left.empty() || !right.empty())
{
if (!left.empty() && left.top()>=low)
{
++ len;
ans = max(ans,low*len);
left.pop();
}
else if (!right.empty() && right.top()>=low)
{
++ len;
ans = max(ans,low*len);
right.pop();
}
else if (!left.empty() && !right.empty())
{
if (left.top() <= right.top())
{
++ len;
low = right.top();
ans = max(ans,low*len);
right.pop();
}
else
{
++ len;
low = left.top();
ans = max(ans,low*len);
left.pop();
}
}
else if (!left.empty())
{
++ len;
low = min(low,left.top());
ans = max(ans,len*low);
left.pop();
}
else if (!right.empty())
{
++ len;
low = min(low,right.top());
ans = max(ans,len*low);
right.pop();
}
}
// cout << l << " " << r << " " << max(max(maxl,maxr),ans) << endl;
return max(max(maxl,maxr),ans);
}
//1506.cpp
#include <iostream>
#include <cstdio>
#include <stack>
using namespace std;
stack<long long> l,r;
#define maxn 100010
long long le[maxn],ri[maxn],a[maxn];
int main()
{
// freopen("1506.in","r",stdin);
// freopen("15062.out","w",stdout);
long long n,i,ans,x;
while (cin >> n && n)
{
while (!l.empty())
l.pop();
while (!r.empty())
r.pop();
for (i=0;i<n;++i)
cin >> a[i];
l.push(0);
le[0] = 0;
r.push(n-1);
ri[n-1] = n-1;
for (i=1;i<n;++i)
{
if (a[l.top()] >= a[i])
{
while (!l.empty() && a[l.top()] >= a[i])
{
x = l.top();
l.pop();
}
le[i] = le[x];
}
else
le[i] = i;
l.push(i);
}
// for (i=0;i<n;++i)
// cout << le[i] << " ";
// cout << endl;
for (i=n-2;i>=0;--i)
{
if (a[r.top()] >= a[i])
{
while (!r.empty() && a[r.top()] >= a[i])
{
x = r.top();
r.pop();
}
ri[i] = ri[x];
}
else
ri[i] = i;
r.push(i);
}
// for (i=0;i<n;++i)
// cout << ri[i] << " ";
ans = 0;
for (i=0;i<n;++i)
ans = max(ans,(ri[i]-le[i]+1)*a[i]);
cout << ans << endl;
}
// fclose(stdin);
// fclose(stdout);
return 0;
}
其实就是做了三遍1505,分别对 a,b,c 的状态进行记录
AC代码如下:
//2870.cpp
#include <iostream>
using namespace std;
#define maxn 1010
#include <cstring>
struct tri
{
short a,b,c;
};
tri h[maxn][maxn],l[maxn][maxn],r[maxn][maxn];
char c[maxn][maxn];
int main()
{
// freopen("2870.in","r",stdin);
// freopen("2870.out","w",stdout);
int m,n,i,j,k,ans;
while (cin >> m >> n)
{
memset(h,0,sizeof(h));
for (i=1;i<=m;++i)
for (j=1;j<=n;++j)
{
cin >> c[i][j];
if (c[i][j] == 'a' || c[i][j] == 'w' || c[i][j] == 'y' || c[i][j] == 'z')
h[i][j].a = h[i-1][j].a + 1;
if (c[i][j] == 'b' || c[i][j] == 'w' || c[i][j] == 'x' || c[i][j] == 'z')
h[i][j].b = h[i-1][j].b + 1;
if (c[i][j] == 'c' || c[i][j] == 'x' || c[i][j] == 'y' || c[i][j] == 'z')
h[i][j].c = h[i-1][j].c + 1;
}
for (i=1;i<=m;++i)
for (j=1;j<=n;++j)
{
if (h[i][j].a)
{
k = j;
while (h[i][k-1].a >= h[i][j].a && (c[i][k-1] == 'a' || c[i][k-1] == 'w' || c[i][k-1] == 'y' || c[i][k-1] == 'z'))
-- k;
l[i][j].a = k;
}
if (h[i][j].b)
{
k = j;
while (h[i][k-1].b >= h[i][j].b && (c[i][k-1] == 'b' || c[i][k-1] == 'w' || c[i][k-1] == 'x' || c[i][k-1] == 'z'))
-- k;
l[i][j].b = k;
}
if (h[i][j].c)
{
k = j;
while (h[i][k-1].c >= h[i][j].c && (c[i][k-1] == 'c' || c[i][k-1] == 'x' || c[i][k-1] == 'y' || c[i][k-1] == 'z'))
-- k;
l[i][j].c = k;
}
}
for (i=1;i<=m;++i)
for (j=1;j<=n;++j)
{
if (h[i][j].a)
{
k = j;
while (h[i][k+1].a >= h[i][j].a && (c[i][k+1] == 'a' || c[i][k+1] == 'w' || c[i][k+1] == 'y' || c[i][k+1] == 'z'))
++ k;
r[i][j].a = k;
}
if (h[i][j].b)
{
k = j;
while (h[i][k+1].b >= h[i][j].b && (c[i][k+1] == 'b' || c[i][k+1] == 'w' || c[i][k+1] == 'x' || c[i][k+1] == 'z'))
++ k;
r[i][j].b = k;
}
if (h[i][j].c)
{
k = j;
while (h[i][k+1].c >= h[i][j].c && (c[i][k+1] == 'c' || c[i][k+1] == 'x' || c[i][k+1] == 'y' || c[i][k+1] == 'z'))
++ k;
r[i][j].c = k;
}
}
ans = 0;
for (i=1;i<=m;++i)
for (j=1;j<=n;++j)
{
if (l[i][j].a)
ans = max(ans,(r[i][j].a-l[i][j].a+1) * h[i][j].a);
if (l[i][j].b)
ans = max(ans,(r[i][j].b-l[i][j].b+1) * h[i][j].b);
if (l[i][j].c)
ans = max(ans,(r[i][j].c-l[i][j].c+1) * h[i][j].c);
}
cout << ans << endl;
}
// fclose(stdin);
// fclose(stdout);
return 0;
}

本文详细解析了三道算法竞赛题目,通过动态规划方法解决二维矩阵和一维数组中的最大子矩阵问题,提供了AC代码示例。
484

被折叠的 条评论
为什么被折叠?



