E. Nanosoft (预处理, 三维dp)

题目: 传送门

题意:

   定义Nanosoft的 logo 为 四个大小相同的正方形合并成一个大正方形。左上角是红色的,右上角是绿色的,左下角是黄色的,右下角是蓝色的。

   例如以下这些都是

   

   以下这些都不是

   给你一个n * m的矩阵,这个矩阵由 4 个大写字母 “R” ,  "G", “Y”, “B” 构成,这四个大写字母分别代表红色,绿色,黄色,蓝色。

      有Q次询问,每次询问, 输入两个坐标,(x1, y1), (x2, y2) 代表 以 (x1, y1) 为矩阵左上角的坐标,(x2, y2)为矩阵右下角的坐标的矩阵中,能作为Nanosoft的 logo的 子矩阵面积最大是多少。

题解:

  首先,定义 pre[ i ][ j ][ col ] 代表矩阵 (1, 1) , (i, j) 中 颜色为 col 的格有多少个, 也就是二维前缀和, 这个可以很简单预处理出来。

  显然, 能作为Nanosoft的 logo 的都必须是正方形, 且边长必须是偶数。

  然后定义 dp[ i ][ j ][ k ] 为 以(i, j) 为右下角, 边长为 k 的正方形中, 能作为Nanosoft的logo的子正方形的边长的最大值, 也就是以 (i - k + 1,  j - k + 1) 为左上角, (i, j)为右下角的正方形中,能作为Nanosoft的 logo的子正方形边长的最大值。

  转移方程就是: dp[ i ][ j ][ k ] = max({ dp[ i ][ j ][ k ], dp[ i ][ j ][k - 1], dp[i - 1][ j ][k - 1], dp[ i ][j - 1][k - 1], dp[i - 1][j - 1][k - 1] });

  我们还可以在o(1)的时间里判断 以 (i,j)为右下角坐标,边长为 k 的正方形是否能整个作为Nanosoft的 logo,能的话,那dp[ i ][ j ][ k ] 就是 k 了。

  然后对于每个询问, 我们知道, 最大的正方形的边长就是 min(x2 - x1 + 1, y2 - y1 + 1);

  那我们可以枚举这样的正方形。 最多每次询问都枚举 max(n, m) 次, 那复杂度就是 n * q 不至于超时。

#include <bits/stdc++.h>
#define LL long long
#define mem(i, j) memset(i, j, sizeof(i))
#define rep(i, j, k) for(int i = j; i <= k; i++)
#define dep(i, j, k) for(int i = k; i >= j; i--)
#define pb push_back
#define make make_pair
#define INF INT_MAX
#define inf LLONG_MAX
#define PI acos(-1)
using namespace std;

const int N = 510;

char a[N][N];
char s[] = "RGYB";
int n, m, q;
int pre[N][N][4]; ///第三维,0代表红色,1代表绿色,2代表黄色,3代表蓝色
int dp[N][N][N];

int check(int lux, int luy, int rdx, int rdy, int col) {
    return pre[rdx][rdy][col] - pre[rdx][luy - 1][col] - pre[lux - 1][rdy][col] + pre[lux - 1][luy - 1][col];
}

void init() {
    rep(k, 0, 3)  rep(i, 1, n)  rep(j, 1, m)
        pre[i][j][k] = pre[i - 1][j][k] + pre[i][j - 1][k] - pre[i - 1][j - 1][k] + (a[i][j] == s[k]);
    rep(i, 1, n) rep(j, 1, m) {
        for(int k = 1; k <= min(i, j); k++) {
            if(k % 2 == 0) {
                int len = k / 2;
                if(check(i - len + 1, j - len + 1, i, j, 3) == len * len
                 &&check(i - len + 1, j - k + 1, i, j - len, 2) == len * len
                 &&check(i - k + 1, j - len + 1, i - len, j, 1) == len * len
                 &&check(i - k + 1, j - k + 1, i - len, j - len, 0) == len * len)
                    dp[i][j][k] = k;
            }
            dp[i][j][k] = max({ dp[i][j][k], dp[i][j][k - 1], dp[i - 1][j][k - 1], dp[i][j - 1][k - 1], dp[i - 1][j - 1][k - 1] });
        }
    }
}

int main() {

    scanf("%d %d %d", &n, &m, &q);
    rep(i, 1, n) scanf("%s", a[i] + 1);
    init();
    while(q--) {
        int x1, x2, y1, y2;
        scanf("%d %d %d %d", &x1, &y1, &x2, &y2);
        int len = min(x2 - x1 + 1, y2 - y1 + 1);
        int ans = 0;
        if(x2 - x1 > y2 - y1) {
            int dis = (x2 - x1) -  (y2 - y1);
            rep(i, 0, dis) ans = max(ans, dp[x2 - i][y2][len]);
        }
        else {
            int dis = (y2 - y1) - (x2 - x1);
            rep(i, 0, dis) ans = max(ans, dp[x2][y2 - i][len]);
        }
        printf("%d\n", ans * ans);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/Willems/p/12307668.html

时间: 2024-10-19 06:33:06

E. Nanosoft (预处理, 三维dp)的相关文章

HDU 4433 locker(三维dp)

这题不太好想啊....我以为是记忆化搜索但是感觉最后的状态不好转移啊.别人都是用三维dp写的,感觉很巧啊. binshen写的:http://www.cnblogs.com/kuangbin/archive/2012/10/27/2742672.html 这题的意思就相当于是一个数字密码锁. 每次可以正向或者反向旋转连续的1-3个数字.求从现在状态转到目标状态需要的最少步数. 题目给了两个长度一样的由0-9组成的字符串.就相当于每次操作可以选择连续的1-3个数字加1或者减1.这不过这个加和减是循

NOJ1859 越野赛 三维DP

题目描述 大戈壁越野赛前后分为很多赛段,一个车队也有多辆车.一个车队在一个赛段只能使用一辆车,但是到达赛段中转站(分隔各赛段的点)时可以任意更换车辆,当然也可以不换.在不同赛段,不同的车有着不同的表现,如果规则不限定换车次数,显然可以在每一个赛段都用最好用的车,但很不幸,换车次数有限定,那么,安排一个合理的换车策略十分重要.我们认为,车队可以选择任意一种车开始赛程,不算换车次数. 现在依次给出从起点到终点N个赛段中每辆车的表现(跑完当前赛段所需时间),请计算出跑完全部赛程所需最短时间 输入 第一

!CodeForces 543A Writing Code --DP--(三维dp,滚动数组)

题意:n个程序员一起写m行代码,第i个程序员每写一行有a[i]个bug,求总bug不超过b的分配方案有多少种 分析:这题很像完全背包,不过求的不是最大/最小bug数,求的是bug数小于上限的分配方案.所以这题要用三维dp dp[i][j][k]表示前i个个程序员总共写了j行代码产生了k个bug时的plan数,这题有两种转移:1)第i个程序员再写一行:2)换第i+1号程序员写 所以方程:dp[i][j][k]=dp[i-1][j][k]+dp[i][j-1][k-a[i]],注意:程序员可能一行都

# codeforces 1272 F. Two Bracket Sequences(三维dp + bfs)

codeforces 1272 F. Two Bracket Sequences(三维dp + bfs) 题目大意 输入两个括号序列 s,t(不一定合法),你需要构造一个尽可能短的合法括号序列使得s,t 都是这个序列的子序列(子序列意味着不用连续) 解题思路 dp[i][j][k]表示匹配到s的第i个字符,匹配到t的第j个字符,并且此时(的个数比)多k个的时候的最小合法序列长度,k的上限是200(s和t中最多200个(或者)). 状态转移: 枚举答案合法序列的每一位是放置(或者) ? 放置(,如

CODEVS 1997 守卫者的挑战(三维dp)

题目很简单,就是一个三维的dp,状态转移方程很好想出来,有一点需要注意这道题目在取的中间过程的时候允许出现背包装不下的情况,只要最后的状态是可以的就行了.注意负数的转移就行了啊. maxn = 210 MLE, 201就AC了啊. 题目描述 Description 打开了黑魔法师Vani的大门,队员们在迷宫般的路上漫无目的地搜寻着关押applepi的监狱的所在地.突然,眼前一道亮光闪过."我,Nizem,是黑魔法圣殿的守卫者.如果你能通过我的挑战,那么你可以带走黑魔法圣殿的地图--"瞬

UVA 12063 Zeros and Ones(三维dp)

题意:给你n.k,问你有多少个n为二进制的数(无前导零)的0与1一样多,且是k的倍数 题解:对于每个k都计算一次dp,dp[i][j][kk][l]表示i位有j个1模k等于kk且第一位为l(0/1) 再次预处理mod[i][j]表示1的i次方模j等于几,具体看代码注释 import java.util.Scanner; public class Main{ static int Maxn=65; static int Maxk=101; //前i个数有j个1模给定的值余k且第一位为1或者0的总个

守卫者的挑战:三维dp

守卫者的挑战 难度级别: C: 编程语言:不限:运行时间限制:1000ms: 运行空间限制:262144KB: 代码长度限制:2000000B 试题描述 打开了黑魔法师 Vani 的大门,队员们在迷宫般的路上漫无目的地搜寻着关押 applepi 的监狱的所在地.突然,眼前一道亮光闪过.“我,Nizem,是黑魔法圣殿的守卫者.如果你能通过我的挑战,那么你可以带走黑魔法圣殿的地图……”瞬间,队员们被传送到了一个擂台上,最初身边有一个容量为 K 的包包.擂台赛一共有 N 项挑战,各项挑战依次进行.第

Happy birthday hdu5234(三维dp)

http://acm.hdu.edu.cn/showproblem.php?pid=5234 题意:一个小姑娘生日了,她妈妈把她领到了一个花园(n*m)里面,起始坐标在(1,1),每个坐标位置都有(a[i][j])的食物,她只能向左或向下走,并且她的胃口最大为K,最终到达(n, m)的位置,问你在满足题意的情况下,她最多能够吃多少食物. 分析:完全照着dp模板写的,没想到1A,百度了一下,没想到大神的做法和我一样.... #include <iostream> #include <std

codevs1169传纸条 不相交路径取最大,四维转三维DP

这个题一个耿直的思路肯定是先模拟.. 但是我们马上发现这是具有后效性的..也就是一个从(1,1)开始走,一个从(n,m)开始走的话 这样在相同的时间点我们就没法判断两个路径是否是相交的 于是在dp写挂了之后..我们妥妥写了一发爆搜..vis的那种 一旦你用了vis数组之后..我们就不能再记忆化搜索了..因为你缺少记录vis数组的状态.. 去了记忆化..来了发纯爆搜..果然T了..但是在codevs上还得了30分..不错不错.. 不错个锤子,ACM就是TLE 贴TLE代码 #include <io