Noip2009模拟题_长方形(rectangle)

题目:
      长方形(rectangle.pas/c/cpp)


【题目描述】


        小明今天突发奇想,想从一张用过的纸中剪出一个长方形。
        为了简化问题,小明做如下的规定:
        (1)这张纸的长度、宽度分别为n,m。小明将这张纸看成是由n*m个格子组成,在剪的时候,只能沿着格子的


边缘剪。
        (2)这张纸有些地方小明以前在上面画过,剪出来的长方形不能含有以前画过的地方。
        (3)剪出来的长方形的大小没有限制。
        小明看着这张纸,想到了好多好多种剪的方法,可是到底有多少种呢?小明数不过来了,你能帮帮他吗?


【输入格式】


        第一行两个正整数,n,m,表示这张纸的长度和宽度。
        接下来有n行,每行m个字符,每个字符为‘*’或者‘.’。
        字符‘*’表示以前在这个格子上画过,字符‘.’表示以前在这个格子上没有画过。


【输出格式】


        仅一个整数,表示方案数。


【输入样例】


        6 4
        ....
        .***
        .*..
        .***
        ...*
        .***


【输出样例】


        38


【数据规模】


        对于10%的数据,满足1<=n<=10,1<=m<=10
        对于30%的数据,满足1<=n<=50,1<=m<=50
        对于60%的数据,满足1<=n<=200,1<=m<=200
        对于100%的数据,满足1<=n<=1000,1<=m<=1000
        最后一个点所有的字符都为‘.’。

算法:枚举

首先说一下本题总共有四种方案。

解法一:
    最朴素的穷举法。先穷举长方形的四条边,时间为O(n^4),然后再判定是否可放,时间为O(n^2),总的时间复杂度
为O(n^6),预计得分为10分。

解法二:(公式不是太理解)
    预处理一个s数组,s[i,j]表示左上方i*j大小的格子中有多少个字符为'*'。那么如果要判断从第i行到第j行,从第
k列到第1列的长方形中有没有字符'*',就可以通过计算s[j,1]+s[i-1,k-1]-s[j,k-1]-s[i-1,1]而得到。如果这个值大
于0,那么说明不可以放,如果等于0,那么可以放。判断的时间被成功地降为O(1)。总的时间复杂度为O(n^4)。预计得
分为30。

解法三:
    这个解法我感觉是思路最清楚的一种方法了,这个解法是在解法二的基础上继续改进的。先穷举长方形的上下两条
边,然后用解法二的方法,判断出在这两条边之间的每一列是否能放。然后从左到右扫一遍,对于某一段连续的能放的
列,如果有k列,那么有k*(k+1) shr 1种不同的放法。穷举的时间为O(n^2),统计的时间为O(n),总时间为O(n^3)。预
计得分为60。

解法四:
    解法四用到了数据结构栈,这个本人不是特别理解,读者参见2006国家集训队论文《基本数据结构在信息学竞赛中

的应用》,本解法预计得分为100。复杂度为O(n^2)。

program rectangle;

const
 maxn=1000;

var
 n,m:longint;
 ans:int64;
 s:array [0..maxn] of ansistring;
 t:array [0..maxn,0..maxn] of longint;
 
procedure init;
var
 i,j:longint;
begin
 readln(n,m);
 for i:=1 to n do readln(s[i]);
 for i:=1 to n do
  begin
   for j:=1 to m do
    begin
     t[i,j]:=t[i-1,j];
     if s[i,j]='*' then inc(t[i,j]);{用t[i,j]表示第i行前j列的*号的个数。}
    end;
  end;
end;

procedure main;
var
 i,j,k,l:longint;
begin
 for i:=1 to n do
  begin
   for j:=i to n do{枚举行。}
    begin
     k:=1;
     while (k<=m) and (t[j,k]<>t[i-1,k]) do inc(k);{略过中间有障碍的列,非常巧妙。}
     while k<=m do
      begin
       l:=k;
       while (l<=m) and (t[j,l]=t[i-1,l]) do inc(l);{寻找无障碍的连续的行。}
       ans:=ans+(l-k)*(l-k+1) shr 1;{利用了一下组合的相关知识,s=k*(k+1) shr 1,貌似很多找规律的题目中都

能见到这个式子。}
       k:=l;
       while (k<=m) and (t[j,k]<>t[i-1,k]) do inc(k);{跳过有障碍的列,再从下一个无障碍的列开始计数。}
      end;
    end;
  end;
end;

begin
 assign(input,'rectangle.in'); reset(input);
 assign(output,'rectangle.out'); rewrite(output);
 
 init;
 main;
 writeln(ans);
 
 close(input); close(output);
end.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值