zoukankan      html  css  js  c++  java
  • 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
    ", 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
    */
  • 相关阅读:
    【线段树】懒标记的维护
    【dp】luoguP4796 关于图 想不到是状压dp (┬_┬)
    【数论】莫比乌斯函数+中国剩余定理
    【积累】Burnside引理和Polya定理
    【排序优化】牛客练习赛54D
    ubuntu修改hostname
    apt-get命令详解
    微信历史版本下载
    vim自动补全快捷键
    Servlet实例
  • 原文地址:https://www.cnblogs.com/fenice/p/5709455.html
Copyright © 2011-2022 走看看