题意:给定一个h*w的网格,里面只有.和#,.表示空的,#表示禁止的,然后有q个询问,询问中给你两个坐标,分别是左上和右下,求在这两者中间的有多少种(竖着和横着)两个相邻的点。
析:一看到这个题目,肯定是DP,一想,不会做,想了好久都不会,这怎么分析呢,横着和竖着明明就是混合的,怎么考虑呢,想了好久都没想出来。后来偷偷问一下别人,哦,原来就是分开考虑的。
分开考虑就是行的考虑行的,列的考虑列的,最后再相加就好了,我们用dr[i][j]来表示第i行到第j个位置的种数,同样的列dc[i][j]第i列到第j个位置的种数。
因为行和列类似,我们就分析行,如果第j-1个是.,那么总数就会多一个,否则总数和和j-1时一样,再考虑一下边界,即:if(G[i][j] == '.' && G[i][j-1] == '.') dr[i][j] = dr[i][j-1] + 1;
else dr[i][j] = dr[i][j-1];
同理列也是如此,
if(G[i][j] == '.' && G[i-1][j] == '.') dc[i][j] = dc[i-1][j] + 1;
else dc[i][j] = dc[i-1][j];剩下的就是如何去从左上角到右下角的满足条件种数了,就是每一行(列)用最后那个坐标减掉最前面那个坐标,这就是这一行(列)的总数,再分别加起来就是最后答案了。
代码如下:
#include <iostream> #include <cstdio> #include <vector> #include <set> #include <queue> #include <iomanip> #include <cstring> #include <sstream> #include <algorithm> #include <map> #include <list> using namespace std; typedef long long LL; const int maxn = 500 + 10; char G[maxn][maxn]; int dr[maxn][maxn], dc[maxn][maxn]; int main(){ int r, c; cin >> r >> c; for(int i = 1; i <= r; ++i) scanf("%s", G[i]+1); memset(dr, 0, sizeof(dr)); memset(dc, 0, sizeof(dc)); for(int i = 1; i <= r; ++i){ for(int j = 1; j <= c; ++j){ if(G[i][j] == '.' && G[i][j-1] == '.') dr[i][j] = dr[i][j-1] + 1; else dr[i][j] = dr[i][j-1]; if(G[i][j] == '.' && G[i-1][j] == '.') dc[i][j] = dc[i-1][j] + 1; else dc[i][j] = dc[i-1][j]; } } int q; cin >> q; while(q--){ int x1, y1, x2, y2; scanf("%d %d %d %d", &x1, &y1, &x2, &y2); int cnt = 0; for(int i = x1; i <= x2; ++i){ cnt += dr[i][y2] - dr[i][y1]; } for(int i = y1; i <= y2; ++i){ cnt += dc[x2][i] - dc[x1][i]; } printf("%d\n", cnt); } return 0; }