旺崽的博客

要么天赋异禀,要么天道酬勤

0%

2021牛客寒假算法基础集训营6 E.网格

题目链接

题目描述

有一个 $n$ 行 $m$ 列的网格,第 $i$ 行 $j$ 列上有数字 $a_{i,j}$ 。每个位置需要从上下左右四个方向中选择互相垂直的两个。

定义 $w(x)=x+popcnt(x)$ ,其中 $popcnt(x)$ 表示 x 的二进制位中 1 的位的数量。

如果两个相邻的位置 $(x1,y_1),(x_2,y_2)$ 互相位于对方选择的某个方向上,则对答案由 $w(a{x1,y_1}\ xor\ a{x_2,y_2})$ 的贡献,其中 $xor$ 表示二进制中的按位异或。

小 Z 想问你答案的最大值是多少。

输入描述:

第一行,输入 $n,m$ 。

接下来 $n$ 行 $m$ 列,输入这个网格。

输出描述:

输出答案的最大值。

示例1

输入

1
2
3
4
3 3 
1 3 6
3 2 4
7 4 0

输出

1
38

动态规划~
因为选定的两个方向必须垂直,所以我们可以将竖直方向和水平方向分开计算,假设先计算水平方向的最大值,对某一行 $i$,用 $dp[j][0/1]$ 表示这一列的最大值,$0$ 表示选左边,$1$ 表示选右边,则有如下状态转移方程:

  • $dp[j][0]=max(dp[j-1][0],dp[j-1][1]+w(a[i][j-1]\oplus a[i][j])$
  • $dp[j][1]=max(dp[j-1][0],dp[j-1][1])$

每一行的最大值就是 $max(dp[m][0],dp[m][1])$,累加即可,对竖直方向同理,AC代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <bits/stdc++.h>

using namespace std;
int n, m, ans, a[1005][1005], dp[1005][2];

int f(int x1, int y1, int x2, int y2) {
return (a[x1][y1] ^ a[x2][y2]) + __builtin_popcount(a[x1][y1] ^ a[x2][y2]);
}

int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) cin >> a[i][j];
for (int i = 1; i <= n; i++) {
for (int j = 2; j <= m; j++) {
dp[j][0] = max(dp[j - 1][0], dp[j - 1][1] + f(i, j - 1, i, j));
dp[j][1] = max(dp[j - 1][0], dp[j - 1][1]);
}
ans += max(dp[m][0], dp[m][1]);
}
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= m; i++) {
for (int j = 2; j <= n; j++) {
dp[j][0] = max(dp[j - 1][0], dp[j - 1][1] + f(j - 1, i, j, i));
dp[j][1] = max(dp[j - 1][0], dp[j - 1][1]);
}
ans += max(dp[m][0], dp[m][1]);
}
cout << ans;
return 0;
}