每日一题,坚持使我强大
今日份快乐:codeforces 463C 传送门
明天份快乐:洛谷 P1219 传送门 (八皇后问题,正好用到这个对角线的知识)
题目大意
一个n*n的棋盘上,每个格子都有一个价值,现在在棋盘上放置两个主教(象),每个主教都可以攻击其所在对角线上的所有点(包括主教所在的点,但只能算一次),并获得其价值,但是两个主教不能攻击同一个格子。
问:如何放置两个主教才能获得最大价值,这两个点在哪,最大打价值是什么。
分析
如果了解过国际象棋的规则就会很简单,国际象棋的棋盘是黑白交替的,每方有两个主教,分别在黑格和白格上。
我们可以发现,如果要满足两个主教不攻击同一个格子,那么这两个主教肯定是一个在黑格上,一个在白格上。这样对角线才不会出现相交,可以用反证法手动验证(让他俩在同一个颜色的格子上)。
在确定这两个主教的位置关系后,我们就要来选择主教的位置了。
科普一个知识:
中从左上角到右下角的对角线叫主对角线
中从右上角到左下角的对角线叫次对角线
对于任意点(x, y),这个点的价值为:主对角线的价值和 + 次对角线的价值和 - 这个点的价值。我们遍历每个点,找到白格的最大值和黑格的最大值,就OK。
现在有两个问题:
- first:如何确定当前点是白格还是黑格
- second:如何计算对角线的和
我们分别来回答这两个问题。
对于问题一:
我们可以发现,对于任意点(x, y),有:
如果 x + y 是偶数,则这个点是白格
如果 x + y 是奇数,则这个点是黑格
我们可以通过 x + y 的奇偶性来判断是黑格还是白格
对于问题二:
我们看一个图
我计算几条次对角线的下标和,我们可以发现,同一条次对角线上的点的下标和相同
(这个图黄色标记的格子都为国际象棋中的白格,他们的下标和都是偶数,和他们相邻一个格子的点下标和肯定都为奇数,也就是黑格)
同样的,主对角线是不是存在这样的性质呢?
通过计算我们可以得到,同一条主对角线上的点的 x - y + n 的值相同
通过这两个结论,我们就可以在输入数据的时候计算对角线的和,再遍历求最大值就OK
接下来就是代码实现了,超详细代码附上
代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll mp[2005][2005] = {0};
ll v_l[4005] = {0}; // 主对角线价值和,要注意 x + y 的最大范围是两倍的 n
ll v_r[4005] = {0}; // 次对角线价值和 ,同样 x - y + n 的最大范围也是两倍的 n
int main() {
ios::sync_with_stdio(false);
int n;
cin >> n;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
cin >> mp[i][j];
v_l[i - j + n] += mp[i][j]; // 计算主对角线价值和
v_r[i + j] += mp[i][j]; // 计算次对角线价值和
}
}
int x1 = 0, x2 = 0, y1 = 0, y2 = 0;
ll mx1 = -1, mx2 = -1;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
ll sum = v_l[i - j + n] + v_r[i + j] - mp[i][j]; // 该点的价值和
if((i + j) % 2 == 0){ // 偶数为白格
if(sum > mx1){
mx1 = sum;
x1 = i;
y1 = j;
}
}
else{ // 奇数为黑格
if(sum > mx2){
mx2 = sum;
x2 = i;
y2 = j;
}
}
}
}
cout << mx1 + mx2 << endl;
cout << x1 << " " << y1 << " " << x2 << " " << y2 << endl;
return 0;
}
欢迎大佬们留言指错