Codeforces Round #249 (Div. 2) D. Special Grid 枚举

题目链接:

http://codeforces.com/contest/435/problem/D

D. Special Grid

time limit per test:4 seconds
memory limit per test:256 megabytes
#### 问题描述 > You are given an n × m grid, some of its nodes are black, the others are white. Moreover, it's not an ordinary grid — each unit square of the grid has painted diagonals. > > The figure below is an example of such grid of size 3 × 5. Four nodes of this grid are black, the other 11 nodes are white. > > > Your task is to count the number of such triangles on the given grid that: > > the corners match the white nodes, and the area is positive; > all sides go along the grid lines (horizontal, vertical or diagonal); > no side contains black nodes. #### 输入 > The first line contains two integers n and m (2 ≤ n, m ≤ 400). Each of the following n lines contain m characters (zeros and ones) — the description of the grid. If the j-th character in the i-th line equals zero, then the node on the i-th horizontal line and on the j-th vertical line is painted white. Otherwise, the node is painted black. > > The horizontal lines are numbered starting from one from top to bottom, the vertical lines are numbered starting from one from left to right.

输出

Print a single integer — the number of required triangles.

样例

sample input
3 5
10000
10010
00001

sample output
20

题意

给你一个网格(网格有对角线),问由网格的边和对角线构成的三角形(三条边上都不能有任何黑点)个数。

题解

其实满足要求的三角形只有等腰直角三角形
对每个点,我们先预处理出八个方向能延伸出去的最大长度。(预处理出来这些,我们就可以判断一个直角三角形的三条边上是否有黑点了。比如对于三角形0 + 3 的斜边,你只要考虑0的左上角能延伸的最大长度或者3的右下角能延伸的最大长度就可以了)
然后枚举每个点,统计以这个点为直角的所有三角形的个数

对8个方向做一个说明:
7   3   4
 \  |  /
  \ | /
   \|/
2---+---0
   /|\
  / | \
 /  |  \
6   1   5

八个方向组成的以'+'为原点,长度为1的直角:
0 -> + -> 1
+-
|

1 -> + -> 2
-+
 |

2 -> + -> 3
 |
-+

3 -> + -> 0
|
+-

4 -> + -> 5
 /
+
 \

5 -> + -> 6
 +
/ \

6 -> + -> 7
\
 +
/

7 -> + -> 4
\ /
 +

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int maxn = 444;

char str[maxn][maxn];

//dp[i][j][d]记录(i,j)顶点从八个方向扩散出去的长度(这里的长度是按顶点数来度量的)。
int dp[maxn][maxn][8];

typedef __int64 LL;

int n, m;

int main() {
	scanf("%d%d", &n, &m);

	for (int i = 1; i <= n; i++) scanf("%s", str[i] + 1);

	for (int i = 0; i < maxn; i++) str[i][0] = str[0][i] = str[n + 1][i] = str[i][m + 1] = '1';

	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			//预处理出每个顶点八个方向能延伸出去的最大长度
			for (int k = 0;; k++) if (str[i][j + k] == '1') {
				dp[i][j][0] = k - 1; break;
			}
			for (int k = 0;; k++) if (str[i + k][j] == '1') {
				dp[i][j][1] = k - 1; break;
			}
			for (int k = 0;; k++) if (str[i][j - k] == '1') {
				dp[i][j][2] = k - 1; break;
			}
			for (int k = 0;; k++) if (str[i - k][j] == '1') {
				dp[i][j][3] = k - 1; break;
			}
			for (int k = 0;; k++) if (str[i - k][j + k] == '1') {
				dp[i][j][4] = k - 1; break;
			}
			for (int k = 0;; k++) if (str[i + k][j + k] == '1') {
				dp[i][j][5] = k - 1; break;
			}
			for (int k = 0;; k++) if (str[i + k][j - k] == '1') {
				dp[i][j][6] = k - 1; break;
			}
			for (int k = 0;; k++) if (str[i - k][j - k] == '1') {
				dp[i][j][7] = k - 1; break;
			}
		}
	}

	int ma = max(n, m);
	LL ans = 0;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			int cnt = ans;
			//这里的k可以理解为两直角边的长度。
			for (int k = 1; k <= ma; k++) {
				//计算以(i,j)这个顶点为直角的等腰直角三角形(总共有八个,看几个是合法的)

				//这四种情况中直角由网格的两条邻边构成
				//这四种情况等腰直角三角形三边跨越的顶点数刚好都是相等的
				if (k <= dp[i][j][0] && k <= dp[i][j][1] && k <= dp[i][j + k][6]) {
					ans++;
				}
				if (k <= dp[i][j][1] && k <= dp[i][j][2] && k <= dp[i + k][j][7]) {
					ans++;
				}
				if (k <= dp[i][j][2] && k <= dp[i][j][3] && k <= dp[i][j - k][4]) {
					ans++;
				}
				if (k <= dp[i][j][3] && k <= dp[i][j][0] && k <= dp[i - k][j][5]) {
					ans++;
				}
				
				//这四种情况中直角由网格的两条相邻对角线构成
				//这里开始斜边跨越的节点数刚好是直角边的两倍,所以斜边是2*k。
				if (k <= dp[i][j][4] && k <= dp[i][j][5] && 2*k <= dp[i - k][j + k][1]) {
					ans++;
				}
				if (k <= dp[i][j][5] && k <= dp[i][j][6] && 2*k <= dp[i + k][j + k][2]) {
					ans++;
				}
				if (k <= dp[i][j][6] && k <= dp[i][j][7] && 2*k <= dp[i + k][j - k][3]) {
					ans++;
				}
				if (k <= dp[i][j][7] && k <= dp[i][j][4] && 2*k <= dp[i - k][j - k][0]) {
					ans++;
				}
			}
		}
	}
	printf("%I64d\n", ans);
	return 0;
}

/*
3 3
001
010
000

3 4
0010
0100
0010

5 5
00000
01010
00000
01010
00000

4 4
0000
0111
0011
0111
*/
posted @ 2016-07-26 23:51  fenicnn  阅读(230)  评论(0编辑  收藏  举报