【牛客】寒假训练营4 G-数三角形 题解

传送门:数三角形
标签:枚举

题目大意

给出一个只由“ * ”和“ . ”组成的n x m的字符矩阵,现在规定其中高为h且顶点坐标为(x,y)的三角形满足以下条件:1、坐标为(x,y),(x+1,y-1),(x+2,y-2)……(x+h-1,y-h+1)的字符都为星号,2、坐标为(x,y),(x+1,y+1),(x+2,y+2)……(x+h-1,y+h-1)的字符都为“ . ”,3、三角形底边所有字符全为星号。另外,同一个星号可以被多个三角形包括,也就是说一个高为3的大三角形包括两个高为2的小三角形。求给出的字符矩阵中有多少个三角形?
输入:第一行两个正整数n,m(1<=n,m<=500),分别代表字符矩阵的行数和列数。接下来n行每行m个字符,代表字符矩阵。
输出:一个正整数,代表字符矩阵中三角形的数量。

算法分析

  • 题目说一个高为3的大三角形包括两个高为2的小三角形,那我们自然会想到先统计大三角形的数量,再逐次减小高度统计小三角形的数量。但这题显然没那么难,观察数据范围我们就会发现,n和m都不超过500,500的三次方只有1e8,也就是说时间复杂度为O(n3)的做法是不会超时的,那我们就可以直接枚举。
  • 枚举什么才能做到不重不漏呢?那当然是顶点。对于一个点,将其斜边向左右两边扩展,遇到不为星号的点则停止,问题就在于每次扩展时如何O(1)判断底边是否合法。我们自然而然会想到前缀和,只要提前预处理出横向的前缀和,就能确定同一行中某段区间中的字符是否全为星号,也就知道底边是否合法。
  • 当然,也可以先枚举高度,再枚举顶点坐标。只不过这种方法需要用到斜边前缀和,也就是在正对角线和副对角线的方向各做一次前缀和,取差分的时候则与普通前缀和一致。值得注意的是,先枚举顶点再利用斜边前缀和进行二分是错误的做法,因为二分的过程中无法限制底边。所以还是要用枚举的做法。若将m看作与n相等,则其时间复杂度是O(n3)。

代码实现

#include <iostream>
using namespace std;
char g[1001][1001];
long long sum[3][1001][1001];
#define lowbit(x) (x)&(-x)
int main(){
	long long dx,dy,p,q,i,j,x,y,x1,y1,n,m,T,mx,ans,k;
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	T=1;
	while(T--){
		cin>>n>>m;
        ans=0LL;
        for(i=1;i<=n;i++)
            for(j=1;j<=m;j++)
                cin>>g[i][j];
        for(i=1;i<=n;i++)
            for(j=m;j>=1;j--)
                sum[0][i][j]=sum[0][i-1][j+1]+(g[i][j]=='*');
        for(i=1;i<=n;i++)
            for(j=1;j<=m;j++){
                sum[1][i][j]=sum[1][i-1][j-1]+(g[i][j]=='*');
                sum[2][i][j]=sum[2][i][j-1]+(g[i][j]=='*');
            }
        for(i=1;i<=n;i++)
            for(j=1;j<=m;j++)
                if(g[i][j]=='*')
                    for(k=1;k<j;k++)
                        if(sum[0][i+k][j-k]-sum[0][i][j]==k&&sum[1][i+k][j+k]-sum[1][i][j]==k&&sum[2][i+k][j+k]-sum[2][i+k][j-k-1]==2*k+1)
                            ans++;
        cout<<ans;
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值