CodeForces 611C New Year and Domino (动态规划,DP)

题意:给定一个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
", cnt);

    }
    return 0;
}
原文地址:https://www.cnblogs.com/dwtfukgv/p/5521309.html