天空中的繁星
【问题描述】
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;
}