UVa 10593: Kites
見事なスパゲッティが茹で上がったので、さらしてみたくなりました。
Description
- . と x からなる n * n 文字のグリッドが与えられます。
- この中に x のみからなる正方形はいくつある?
- 正方形は軸平行か45度回転。つまりこんな感じ。
xxx ..x..
xxx .xxx.
xxx xxxxx
.xxx.
..x..
- n <= 500
Algorithm
- Ad-hoc.
- 軸平行なものを数えるには、
- 各マスについて、右側に x がいくつ連続しているか をあらかじめ求めておく。(O(n2))
- あるマスを左上とする正方形の数は、この値の min を取りながら下方向に見ていけばよい。
- 比較的簡単。
- 45度を数えるには、
- 各マスについて、{右,左,上,下}側に x がいくつ連続しているか をあらかじめ求めておく。
- あるマスを中心とする正方形の数は、上下の min を取りながら左右に、また左右の min を取りながら上下に見ていけばよい。
- 超ややこしい。
- 全体として O(n3)。
- n <= 500 なので危ない気もしたけど、定数項が軽いせいか 1.044/4.000 [sec] で通った。
Code
#include <iostream> #include <sstream> #include <queue> #include <map> #include <set> #include <vector> #include <numeric> #include <algorithm> #include <string> #include <cassert> #include <cstdlib> #include <cmath> #include <cstdio> #include <cstring> #define REP(i,e) for(int i=0;i<(int)(e);i++) #define FOR(i,b,e) for(int i=(int)(b);i<(int)(e);i++) using namespace std; template<class T>void ls(T& a,T b){ if(b<a) a=b; } const int N=512; char v[N][N]; int l[N][N]; int r[N][N]; int u[N][N]; int d[N][N]; int square(int n){ int ans=0; int k,t; REP(i,n) REP(j,n){ for(k=1,t=r[i][j];t>=k;t=min(t,r[i+k][j]),k++){} if(k>=2) ans+=k-2; } return ans; } int diamond(int n){ int ans=0; REP(i,n) REP(j,n){ int t=min(min(l[i][j],r[i][j]),min(u[i][j],d[i][j])),k; for(k=1;0<=i-k && i+k<n && 0<=j-k && j+k<n && t>1;k++) t=min(t,k-1+min(min(min(l[i-k][j],r[i-k][j]),min(l[i+k][j],r[i+k][j])), min(min(u[i][j-k],d[i][j-k]),min(u[i][j+k],d[i][j+k])))); if(k>=2) ans+=min(t,k-1); } return ans; } int brute_force(int n){ if(n==500) return 0; int a=0,b=0; REP(i,n) REP(j,n) FOR(k,2,n+1){ int cnt=0; FOR(ii,i,i+k) FOR(jj,j,j+k) if(v[ii][jj]=='x') cnt++; if(cnt==k*k) a++; } REP(i,n) REP(j,n) FOR(k,1,n+1){ FOR(ii,i-k,i+k+1) FOR(jj,j-k,j+k+1){ if(!(0<=ii && ii<n && 0<=jj && jj<n)) goto bad; if(abs(ii-i)+abs(jj-j)<=k && v[ii][jj]!='x') goto bad; } b++; bad:; } return a+b; } int main(){ int n; while(scanf("%d",&n)!=EOF){ memset(v,'.',sizeof v); REP(i,n) scanf("%s",v[i]); memset(l,0,sizeof l); memset(r,0,sizeof r); memset(u,0,sizeof u); memset(d,0,sizeof d); REP(i,n){ int cnt; cnt=0; for(int j=n-1;j>=0;j--){ cnt=(v[i][j]=='x' ? cnt+1 : 0); r[i][j]=cnt; } cnt=0; for(int j=n-1;j>=0;j--){ cnt=(v[j][i]=='x' ? cnt+1 : 0); d[j][i]=cnt; } cnt=0; REP(j,n) { cnt=(v[i][j]=='x' ? cnt+1 : 0); l[i][j]=cnt; } cnt=0; REP(j,n) { cnt=(v[j][i]=='x' ? cnt+1 : 0); u[j][i]=cnt; } } cout << square(n) + diamond(n) << endl; } }
- デバッグ用の brute-force もおまけに付いております。
- diamond (45度の方) があまりにも謎過ぎる。minばっかりでひどい。
- O(n3) より速くできないものだろうか。
- ちょっとだけ考えてみたけど、よく分からなかったです。