『Citric』天空中的繁星 · DP

天空中的繁星

 

【问题描述】

Lemon最近买了一台数码相机。某天Lemon很无聊,于是对着夜空拍了一张照片,然后把照片导入了电脑。

Lemon想依靠电脑的力量,完成他小时候经常做却从来没有成功过的事情:数天空中有多少颗星星。

Lemon已经把相片处理成了黑白的,也就是说,每个像素只可能是两个颜色之一,白或黑。Lemon定义像素(x,y)处是一颗星星,当且仅当,像素(x,y),(x-1,y),(x+1,y),(x,y-1),(x,y+1)都是白色的。因此一个白色像素有可能属于多个星星,也有可能有的白色像素不属于任何一颗星星。

借助电脑的力量,数出有多少颗星星对Lemon实在太容易了,他很快就完成了。

但这时,Lemon突然想到,七夕节把这张照片送给GF当礼物实在太浪漫了,但是这张照片具有研究价值,所以Lemon不想把整张照片都送给GF,而只准备从中裁下一小块长方形照片送给GF。但为了保证浪漫的效果,Lemon认为,他送给GF的那一小块相片中至少应该有k颗星星。

现在Lemon想知道,到底有多少种方法裁下这一小块长方形相片呢?

 

【输入格式】

输入文件中的第一行为三个正整数n,m,k,意义见题目所示。

接下来的n行,每行为一个长度为m的字符串,字符串仅由'.'和'*'构成,其中:'.'表示这个像素为黑色,'*'表示这个像素为白色。

 

【输出格式】

输出文件中仅一行为一个整数,表示Lemon有多少种满足题意的裁剪方法。

 

【输入输出样例】

输入:

5 6 3

***...

****..

.**.*.

******

.*.***

 

输出:

3

 

【样例说明】

图中共有4颗星星,分别位于第2行第2列、第2行第3列、第4行第2列、第4行第5列。

有3种符合题意的选择方法(以左上角行列 - 右下角行列方式给出): (1,1)-(5,4),(1,1)-(5,5),(1,1)-(5,6)。

 

【数据范围】

对于20%的数据,满足:n,m<=20;

对于40%的数据,满足:n,m<=100;

对于70%的数据,满足:n,m<=200;

对于100%的数据,满足:n,m<=500,0<k<n*m。


【算法分析】

大意是求在一个01矩阵中划出

首先这题按照数据范围,几种级别的算法非常明显。

20%,O(n^6),直接六方暴力,枚举左上和右下然后二维扫描。

40%,O(n^4),用前缀和f[i][j]表示从(1,1)到(i,j)这样一个矩形内有多少个星星,就可以O(1)计算出一个矩形的星星数量。

70%,O(n^3 logn),对于40%的方法,可以很明显的发现,如果我们确定了上界、下界和右边界,那么可行的左边界一定是连续的一段,所以我们只要二分查找满足条件的最大的左边界left在哪里,那么从1到left都是满足条件的。

100%,O(n^3),对于70%的算法,如果我们确定了上下边界,那么如果右边界递增的话,那么满足条件的最大左边界一定是非递减的,所以我们维护一个滑窗left,right表示右边界在right时的最大左边界left,每次right增加的时候对left更新,可以将二分那个logn的复杂度给去掉。

#include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std;
#define rep(i,n) for (int i=1;i<=n;i++)
#define calc(i,j,p,q) (s[p][q]-s[p][j-1]-s[i-1][q]+s[i-1][j-1])

const int N=605;
int n,m,a[N][N],f[N][N],s[N][N],k;
long long ans;
int l,r;

int find(int i,int p,int q){
	int l=1,r=q,ret=0;
	while (l<=r){
		int j=(l+r)>>1;
		if (calc(i,j,p,q)>=k) ret=j,l=j+1;		
			else r=j-1;
	}
	return ret;
}

int main(){
	freopen("star.in","r",stdin);
	freopen("star.out","w",stdout);
	scanf("%d%d%d",&n,&m,&k);getchar();
	rep(i,n){
		rep(j,m) if (getchar()=='*') a[i][j]=1;
		getchar();
	}
	n-=2;m-=2;
	rep(i,n) rep(j,m)
		if (a[i][j+1] && a[i+1][j] && a[i+1][j+1] && a[i+1][j+2] && a[i+2][j+1]) f[i][j]=1;	
	rep(i,n) rep(j,m) s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+f[i][j];
	rep(i,n) rep(p,i){
		l=1;
		for (r=1;r<=m && calc(p,l,i,r)<k;r++);
		for (;r<=m;r++){
			for (;calc(p,l,i,r)>=k;l++);
			ans+=(long long)l-1;
		}
	}
	printf("%I64d\n",ans);
	return 0;
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值