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\n", cnt);

    }
    return 0;
}
时间: 2024-08-07 04:33:46

CodeForces 611C New Year and Domino (动态规划,dp)的相关文章

Codeforces 611C New Year and Domino DP+容斥

"#"代表不能放骨牌的地方,"."是可以放 500*500的矩阵,q次询问 开两个dp数组,a,b,a统计横着放的方案数,b表示竖着放,然后询问时O(1)的,容斥一下, 复杂度O(n^2+q) #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<cstdlib> #include<cmat

Codeforces 611C New Year and Domino(二维前缀和)

题目大概说给一个n*m个格子,格子'.'表示可以放东西,多次询问矩形区域(x1,y1)-(x2,y2)有几种放一张1*2的骨牌的方案数. 分别考虑横着竖着放,预处理出二维的前缀和,即sum[x][y]表示(1,1)-(x,y)的横着或者竖着放的方案数,然后对于每一个询问就拆成几个前缀和容斥一下.. 细节要注意..骨牌是1*2的.. 1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 char map[555

Codeforces 506E Mr. Kitayuta&#39;s Gift - 动态规划 - 矩阵

题目传送门 通往Codeforces的航线 通往vjudge的航线 题目大意 给定一个仅包含小写字母的串$s$,要求插入恰好$n$个小写字母字符,使得新串为回文串.问这样得到的新串有多少个是本质不同回文串. $1\leqslant |s| \leqslant 200,1 \leqslant n \leqslant 10^{9} $ 神题orz...orz...orz.Petr orz...orz...orz. 好了开始扯正题了. 任意位置插入并不太好处理.可以转化一下这个问题,求一个长度为$|s

Fibonacci斐波拉契数列----------动态规划DP

n==10 20 30 40 50 46 体验一下,感受一下,运行时间 #include <stdio.h>int fib(int n){ if (n<=1)     return 1; else            return fib(n-1)+fib(n-2); }int main( ){ int n; scanf("%d",&n); printf("%d\n" ,fib(n) );} 先 n==10 20 30 40 50 46

动态规划(DP),类似LIS,FatMouse&#39;s Speed

题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1108 解题报告: 1.首先按照weight从小到大排列,weight相同的按照speed从大到小排列; 2.Count[i]表示到第i个老鼠时,所求的最长“速度递减”子序列的长度: 3.path[i]=j是题目的关键,记录在Count[i]=Count[j]时,即最长“速度递减”子序列最后一个老鼠的前一只老鼠的位置 4.递归输出id void output(in

(RQoj 15 采药------rwkj 10.1.5.253 1447) 动态规划 DP 1

#include <iostream>#include <string.h>using namespace std;int dp[105][1005], w[105],v[105],T,M;int max(int x,int y){ return x>y?x:y; } void f( ){ int i,j; for (i=1; i<=M; i++) for (j=0;j<=T; j++) { if (i==0) dp[i][j]=0; else dp[i][j]=

(RQoj 15 采药------rwkj 10.1.5.253 1447) 动态规划 DP 2

70 371 10069 11 2 #include <iostream>#include <string.h>using namespace std;int dp[105][1005], w[105],v[105],T,M;int max(int x,int y){ return x>y?x:y; }void f( ){ int i,j; for (i=M; i>=1; i--) for (j=0;j<=T; j++) { if (i==M+1) dp[i][j

(RQoj 15 采药------rwkj 10.1.5.253 1447) 动态规划 DP 3

#include <iostream>#include <string.h>using namespace std;int dp[1005], w[105],v[105],T,M;int max(int x,int y) { return x>y?x:y; }void f( ){ int i,j; for (i=1; i<=M; i++) for (j=T;j>=0; j--) if (j>=w[i]) dp[j]=max(dp[j],dp[j-w[i]]+

动态规划 DP

动态规划 DP 我们用f[ i ] 表示从 i 点出发到达终点的最多能休息的时间 然后我们发现 状态转移方程f[ i ] = f[ i+1 ] +1 ; 当该点 并没有工作计划时 f[ i ] = max(f[ i+len ],f[ i ]); 当该点 有工作计划时 一个或若干个 1 #include <bits/stdc++.h> 2 #define For(i,j,k) for(int i=j;i<=k;i++) 3 using namespace std ; 4 5 const i