数字8(eight )题解

题目描述

Farmer的奶牛近收到一块大理石。但这块块石头有些不完整。

为了说明这块石头的状况,我们就可以用一个N*N正方形网格(5<= N <=300)来描述,其中字符’*’代表石头的缺损部分,’.’表示石头完美无瑕的部分。奶牛要在这块大理石上雕刻一个数字”8”。

然而,它们也需要你的帮助,以确定数字8在这块大理石上最佳的雕刻位置。这里有几个要求来定义一个有效的数字8:

*数字8由上下两个矩形构成。

*数字8的上下两个矩形都满足至少有一个单元格在矩形内部。

*数字8顶部的矩形的底边必须为底部矩形顶边的子集。

*数字8只能刻在大理石完美无瑕的部分。

*规定数字8的得分为上矩形和下矩形的面积的乘积,它们希望得分能达到最大。

例如,给出这样的一块大理石:
数字8(eight )题解_第1张图片
最优的8摆放位置如下图所示:
数字8(eight )题解_第2张图片
上矩形的面积为6*9=54,下矩形的面积则是12*6=72。所以,它的得分就是54*72=3888。

输入

第一包括一个整数N,代表大理石的边长。

第二到N+1行,每一行描述了大理石的一行,包含着N个字符,每一个字符是”*”(代表缺损的部分)与”.”(代表完美无瑕的部分)其中一个

输出

第一包含一个整数,代表符合条件数字8中最大的得分。若没有一个合法的数字8,请输出-1。

样例输入

15
……………
……………
*…..
.….…….*
.……….*.
….*……….
…***….
……………
..*.....
…*.*….
..…*…….
……………
…....……
………*…..
……………

样例输出

3888

提示

对于30% 的数据,5<=N<=80。

对于100%的数据,5<=N<=300。

想法

  • 上下可以分开考虑
  • 最优化问题考虑动态规划
  • 数据范围在空间与时间上都考虑O(n^3)的算法

算法

  • f[i][l][r]表示第i行 选取l为左端点 r为右端点的线段为下边的上方矩形的最大面积
  • g[i][l][r]表示第i行 选取l为左端点 r为右端点的线段为上边的下方矩形的最大面积
  • 显然f g都是可以在O(n^3)的时间内处理出来
  • 以f为例 从上到下扫描 找到最上面的符合要求的上边
  • 根据题目要求 上矩形下边要为下矩形上边的子集 考虑改变f数组的意义
  • f[i][l][r]改为表示f[i][l][r] 第i行 左右端点分别在[l,r]区间内的最大矩形面积
  • 显然f[i][l][r]=max(f[i][l][r],f[i][l+1][r],f[i][l][r-1]);

代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
using namespace std;
int map[310][310],n,sum[310][310],f[310][310][310],g[310][310][310];
typedef long long ll;
ll ans=-1,cnt;
char ch;
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
        for (int j=1;j<=n;j++)
        {
            scanf(" %c",&ch);
            map[i][j]=(ch=='*');
            sum[i][j]=sum[i][j-1]+map[i][j];
        }
    for (int i=1;i<=n-2;i++)
        for (int j=i+2;j<=n;j++)
        {
            int p=0;
            for (int k=1;k<=n;k++)
            {
                if(map[k][i]||map[k][j])p=0;
                if(!(sum[k][j]-sum[k][i-1]))
                    p?f[k][i][j]=(j-i-1)*(k-p-1):p=k;
            }
            p=0;
            for (int k=n;k>=1;k--)
            {
                if(map[k][i]||map[k][j])p=0;
                if(!(sum[k][j]-sum[k][i-1]))
                    p?g[k][i][j]=(j-i-1)*(p-k-1):p=k;
            }
        }
    for (int k=1;k<=n;k++)
    {
        for (int i=1;i<n-2;i++)
            for (int j=i+3;j<=n&&!(sum[k][j]-sum[k][i-1]);j++)
                f[k][i][j]=max(f[k][i][j],f[k][i][j-1]);
        for (int j=n;j>3;j--)
            for (int i=j-3;i>=0&&!(sum[k][j]-sum[k][i-1]);i--)
                f[k][i][j]=max(f[k][i][j],f[k][i+1][j]);
    }
    for (int k=3;k<n;k++)
    {
        for (int i=1;i<n-1;i++)
            for (int j=i+2;j<=n;j++)
            {
                cnt=f[k][i][j]*g[k][i][j];
                if(!(sum[k][j]-sum[k][i-1])&&cnt)ans=max(ans,cnt);
            }
    }
    cout<<ans<<endl;
    return 0;
}

你可能感兴趣的:(数字8(eight )题解)