刷题Day9统计方形

本文介绍了如何使用动态规划策略解决计算给定矩形区域内正方形和长方形数量的问题。作者首先尝试暴力枚举但遇到超时错误,然后转向动态规划方法,通过递推公式减少了空间复杂度。最终,给出了AC代码实现,详细阐述了计算正方形和长方形数量的递推关系,并成功解决了问题。
摘要由CSDN通过智能技术生成

前言

 前段时间放假就没怎么刷题了,今天开始继续刷题。先放题目链接:
P2241 统计方形(数据加强版)
在这里插入图片描述
我的解题思路:
 本题被洛谷归在了暴力枚举门类下,作为小白,一开始我还是踩坑了的。开始我还真一个点一个点枚举过来然后算长方形和正方形数量,然后不出意外的TLE了。所以看似是暴力枚举,但是其实是有技巧在其中的。
 我最早想到的技巧的动态规划。假设D1(u,v)表示长宽分别为u和v的矩形包含的正方形数量,那么求D1(u,v)可以通过D1(u,v)来求。理论上确实可行,但是这里n和m取值为1-5000,这样的话在n,m较大的时候很容易造成实际数量超过int范围而需要long int 甚至long long int(C99标准)。实际上在求长方形数量的时候确实会超int位长。这样记录正方形和长方形的数量的递推矩阵不可避免的要用long long int类型,这样的话又会造成MLE。
 这样的话,仔细思考一番,利用动态规划的思路来做,其实不太需要把整个矩阵的存下来,通过D1(1,1)->D1(1,2)->…->D1(1,m)->D1(2,m)->D1(3,m)->…->D1(n,m)来求其实也可以,这样的话其实就是用一个变量来记录数量就行了。
 因为不太好把草稿搬到上面来,我也不会画出那样的图,所以我就只能记录一下简要的递推式得出的思路:
 (1)假设D1(u,v)为长宽为u和v的矩形包含的正方形数量。只考虑D1(u+1,v)和D1(u,v)的关系,这里,D1(u+1,v)比D1(u,v)多的是多出来一个1*v的矩形,因此,目前二者关系可以归结为D1(u+1,v)=D1(u,v)+add(1*v)。这个add(1*v)的表达式就是要找的。而1*v多出来的正方形数量,就是包含部分1*v内小正方形的所有正方形数量。这里,1*1的新增正方形数量为v,2*2的新增正方形数量为(v-1)…往后就要考虑v和u的大小关系了。当u<v的时候,正方形最大为u*u,这时候只能加到v-u个,因此add(1*v)此时就是v+(v-1)+…+(v-u)= ( 2 v − u ) ( u + 1 ) 2 \frac{(2v-u)(u+1)}{2} 2(2vu)(u+1);同时,若u ≥ \ge v,那么加和就可以一直到1,则此时add(1*v)即为 v ( v + 1 ) 2 \frac{v(v+1)}{2} 2v(v+1)。由于D1(u,v+1)和D1(u,v)的关系本质同山因此不讨论了。
 (2)假设D2(u,v)为长宽为u和v的矩形包含的长方形数量(按照题目意思应该不认为正方形是特殊的长方形)。按照上述思路一样来讨论。这里依旧讨论D2(u+1,v)和D2(u,v)的关系。前者比后者多了一个1*v的小矩形。讨论新增长方形数量有点小复杂,先从新增小矩形导致的新增长方形长或者宽为1开始看,这样就有(u+1-1)*v=u*v个;为2则有(u+1-1)*(v-1)个…同理需要讨论u和v的大小关系,当u>v的时候,加和一直到(u+1-1)*1个,因此总加和为 u v ( v + 1 ) 2 \frac{uv(v+1)}{2} 2uv(v+1)反之,当新增长方形的长/宽超过u以后,则一直都是长方形,即为(u+1)*(v-u)+(u+1)*(v-u-1)+…+(u+1)*1,因此总加和是 ( 2 v − u + 1 ) u 2 2 + ( v − u + 1 ) ( v − u ) 2 \frac{(2v-u+1)u^2}{2}+\frac{(v-u+1)(v-u)}{2} 2(2vu+1)u2+2(vu+1)(vu)
 利用上述递推关系去求D1(n,m)和D2(n,m)就可以了,具体方向如一开始说的那样。最后是我的AC代码:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main(int argc, char *argv[]) {
	long long int n,m;
	scanf("%lld%lld",&n,&m);
	long long int i,j;
	long long int matrix[n+1][m+1],matrix2[n+1][m+1];
	long long int sum_squ=1,sum_rec=0;

	for(j=2;j<=m;j++){
		sum_squ=sum_squ+1;}
	j=m;
	for(i=2;i<=n;i++){
		if(i-1>=j)
			sum_squ=sum_squ+j*(1+j)/2;
		else
			sum_squ=sum_squ+(2*j-i+1)*i/2;
		}

	for(j=2;j<=m;j++){
		sum_rec=sum_rec+j-1;
	}
	j=m;
	for(i=2;i<=n;i++){
		if(i-1<j){
			sum_rec=sum_rec+(2*j-i+2)*(i-1)*(i-1)/2+i*(j-i+1)*(j-i+2)/2-(j-i+1);
		}else{
			sum_rec=sum_rec+j*(i-1)*(j+1)/2;
		}
	}

	printf("%lld %lld",sum_squ,sum_rec);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值