【vijos】1764 Dual Matrices(dp)

https://vijos.org/p/1764

自从心态好了很多后,做题的确很轻松。

这种题直接考虑我当前拿了一个,剩余空间最大能拿多少即可。

显然我们枚举每一个点拿出一个矩形(这个点作为右下角),然后剩余空间我们只需要考虑i+1~n和j+1~m这一块空间即可(至于为什么多想想QAQ)

所以我们维护i+1~n和j+1~m能取到的最大矩形即可。

显然二维前缀和,然后再维护一个二维

mx[i][j]=max{get(i, j), mx[i+1][j], mx[i][j+1]},然后每一次找一个点i,j时只需要加上max{mx[i+1][1], mx[1][j+1]}即可。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <string>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
#define rep(i, n) for(int i=0; i<(n); ++i)
#define for1(i,a,n) for(int i=(a);i<=(n);++i)
#define for2(i,a,n) for(int i=(a);i<(n);++i)
#define for3(i,a,n) for(int i=(a);i>=(n);--i)
#define for4(i,a,n) for(int i=(a);i>(n);--i)
#define CC(i,a) memset(i,a,sizeof(i))
#define read(a) a=getint()
#define print(a) printf("%d", a)
#define dbg(x) cout << (#x) << " = " << (x) << endl
#define printarr2(a, b, c) for1(_, 1, b) { for1(__, 1, c) cout << a[_][__]; cout << endl; }
#define printarr1(a, b) for1(_, 1, b) cout << a[_] << ‘\t‘; cout << endl
inline const int getint() { int r=0, k=1; char c=getchar(); for(; c<‘0‘||c>‘9‘; c=getchar()) if(c==‘-‘) k=-1; for(; c>=‘0‘&&c<=‘9‘; c=getchar()) r=r*10+c-‘0‘; return k*r; }
inline const int max(const int &a, const int &b) { return a>b?a:b; }
inline const int min(const int &a, const int &b) { return a<b?a:b; }

const int N=1005, oo=~0u>>2;
int arr[N][N], sum[2][N][N], mx[N][N], n, m, a, b, ans=-oo;

int get(int x, int y, int k) {
	int ret=-oo, fx=x-(k*a)+k, fy=y-(k*b)+k, now=(k==1?0:1);
	if(fx>=1 && fy>=1 && fx<=n && fy<=m) ret=max(ret, sum[now][x][y]-sum[now][fx-k][y]-sum[now][x][fy-k]+sum[now][fx-k][fy-k]);
	fx=x-(k*b)+k, fy=y-(k*a)+k;
	if(fx>=1 && fy>=1 && fx<=n && fy<=m) ret=max(ret, sum[now][x][y]-sum[now][fx-k][y]-sum[now][x][fy-k]+sum[now][fx-k][fy-k]);
	return ret;
}
int main() {
	read(n); read(m); read(a); read(b);
	for1(i, 1, n) for1(j, 1, m) read(arr[i][j]);
	for1(i, 1, n) for1(j, 1, m) sum[0][i][j]=sum[0][i-1][j]+sum[0][i][j-1]-sum[0][i-1][j-1]+arr[i][j];
	for3(i, n, 1) for3(j, m, 1) sum[1][i][j]=sum[1][i+1][j]+sum[1][i][j+1]-sum[1][i+1][j+1]+arr[i][j];
	for3(i, n+1, 0) for3(j, m+1, 0) mx[i][j]=-oo;
	for3(i, n, 1) for3(j, m, 1) mx[i][j]=max(get(i, j, -1), max(mx[i][j+1], mx[i+1][j]));
	for1(i, 1, n) for1(j, 1, m) ans=max(ans, get(i, j, 1)+max(mx[i+1][1], mx[1][j+1]));
	if(ans==-oo) puts("Impossible");
	else print(ans);
	return 0;
}

描述

一个N行M列的二维矩阵,矩阵的每个位置上是一个绝对值不超过1000的整数。

你需要找到两个不相交的A*B的矩形,使得这两个矩形包含的元素之和尽量大。

注:A*B的矩形指连续的A行、B列的子矩阵,或连续的B行、A列的子矩阵。不相交指两个矩形无公共元素。

格式

输入格式

第一行包括4个正整数N,M, A, B,之间由空格隔开。

以下N行,每行M个整数,之间由空格隔开,依次表示二维矩阵的每个元素。

输出格式

输出一行,一个整数,表示答案。如果找不到两个不相交的A*B的矩形,输出"Impossible"。

样例1

样例输入1[复制]

3 4 1 2
1 -1 3 4
-1 9 -1 1
9 8 5 2

样例输出1[复制]

25

限制

1s

提示

30%的数据满足,1 <= N, M <= 50.
另外30%的数据满足,A = B.
100%的数据满足,1 <= N, M <= 1 000, 1 <= A, B <= N.

时间: 2024-10-13 22:46:07

【vijos】1764 Dual Matrices(dp)的相关文章

【vijos】1757 逆序对(dp)

https://vijos.org/p/1757 有时候自己sb真的是不好说... 我竟然想了半天都没想到这个转移. 我是有多傻.... 我们设f[i][j]表示1~i的排列且逆序对恰好是j的方案数. 显然我们只需要将i放进i-1排列中就行了. 而且发现i始终大于i-1 那么就好做了,我们只要将所有i放到i-1序列的位置的方案全都加起来即可,即: f[i][j]=sum{f[i-1][k], max{0, j-i+1}<=k<=j} 用前缀和搞搞就行了. #include <cstdio

【BZOJ】1068: [SCOI2007]压缩(dp)

http://www.lydsy.com/JudgeOnline/problem.php?id=1068 发现如果只设一维的话无法转移 那么我们开第二维,发现对于前i个来说,如果确定了M在哪里,第i个是用R还是不用就能确定了(如果用R那么在中间一定变成了缓冲串) 那么可以转移了 设d[i,j]表示前i个串,最近的一个M在i的前边一个格子,的最短长度,有 d[1,1]=1 d[i,i]=min{d[i-1,j]}+2 //即用一次M又补上i,所以+2 d[i,j]=d[pos,j]+1,其中pos

【bzoj1925】地精部落[SDOI2010](dp)

题目传送门:1925: [Sdoi2010]地精部落 这道题,,,首先可以一眼看出他是要我们求由1~n的排列组成,并且抖来抖去的序列的方案数.然后再看一眼数据范围,,,似乎是O(n^2)的dp?然后各种撕烤,,,然而还是不会... 对于这道题,我第一眼的想法是用f[i][j]表示长度为i,最后一个数是j的抖动序列的方案数,然而这是个1~n的排列,似乎没法解决数字重复的问题..QAQ 于是看了一波题解,,,原来这个dp是这样子搞的:用f[i][j]表示i个数的排列.第一个数<=j且开头下降的抖动序

【51nod1519】拆方块[Codeforces](dp)

题目传送门:1519 拆方块 首先,我们可以发现,如果第i堆方块被消除,只有三种情况: 1.第i-1堆方块全部被消除: 2.第i+1堆方块全部被消除:(因为两侧的方块能够保护这一堆方块在两侧不暴露) 3.第i堆方块过了h[i]次操作后,从上到下被消除. 于是我们设l[i]为第i堆方块从左边开始消除的最小操作次数,设r[i]从右边开始消除的最小操作次数. 然后从左向右dp出l[i],从右向左dp出r[i],然后就能算答案了. 代码: #include<cstdio> #include<cs

【uva-11584】Partitioning by Palindromes(dp)

粗略的复杂度是L^3,长度最大是1000,,没敢做,之后发现其实这个复杂度的系数也不大,可以过,而且很快. dp[j] = dp[i - 1] + 1 (if(str[i] ~ str[j]为回文) 14327451 11584 Partitioning by Palindromes Accepted C++ 0.052 2014-10-09 09:33:17 #include<set> #include<map> #include<stack> #include<

【Luogu】P2331最大子矩阵(DP)

题目链接 这题的状态转移方程真是粗鄙. f[i][j][k]表示前i行用了j个矩阵状态为k的时候的最大值. k=0:两列都不选. k=1:取左弃右. k=2:选右弃左. k=3:左右都选,但分属于两个独立矩阵. k=4:左右都选,且同属于一个矩阵. 参考题解:孤寂的时代 代码 #include<cstdio> #include<cctype> #include<cstring> inline long long max(long long x,long long y){

【UVa】Partitioning by Palindromes(dp)

http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=27&page=show_problem&problem=2549 设w[i,j]为i-j能分割成的最少回文串 f[i]为前i个字符能够分成的最少回文串 w[i,j]=1 当w[i+1,j-1]==1 && s[i]==s[j] 或 i==j-1 && s[i]==s[j] w[i,j]=

【Luogu】P2051中国象棋(DP)

题目链接 去看STDCALL的题解吧 #include<cstdio> #include<cctype> #define mod 9999973 inline long long read(){ long long num=0,f=1; char ch=getchar(); while(!isdigit(ch)){ if(ch=='-') f=-1; ch=getchar(); } while(isdigit(ch)){ num=num*10+ch-'0'; ch=getchar(

【AGC030F】Permutation and Minimum(DP)

题目链接 题解 首先可以想到分组后,去掉两边都填了数的组. 然后就会剩下\((-1,-1)\)和\((-1,x)\)或\((x,-1)\)这两种情况 因为是最小值序列的情况数,我们可以考虑从大到小填数,一个组填完了最小值就是当前填的数 设\(f[i][j][k]\)表示已经填到数\(i\)剩余\(j+k\)个填了一个数的组,其中有\(j\)个组是第一种情况填出来的,\(k\)个是第二种情况来的 分情况转移 如果这个数属于第一种情况,那么可以转移到: \(f[i][j+1][k]:\)填到一个\(