codeforces 463C. Gargari and Bishops 解题报告
题目链接:http://codeforces.com/contest/463/problem/C
题目意思:要在一个 n * n 大小的棋盘上放置两个bishop,bishop可以攻击的所有位置是包括经过bishop 位置的两条成90度的对角线所经过的所有坐标点。每个坐标点都有数字在上面,放置一个bishop后,可以获得能被放置的bishop攻击的所有坐标点的数字的和。但是有一个条件限制:同一个坐标点不能被两个bishop攻击,也就是四条对角线的交点不能是棋盘上的某个坐标点。求出在该条件限制下,两个bishop的放置位置以及能获得的最大和。
首先没有看到这个条件wa了很多次: place two bishops on the chessboard in such a way that there is no cell that is attacked by both of them。
好不容易看到之后,就各种TLE了,原来是读入问题!!!!涨姿势勒= =
可喜的是,原来自己最开始的做法是可取的,不过都是因为读入问题。
先介绍作者的灵活高效版:
先看一个图:
作者的做法有两个巧妙之处:
(1)求 bishop 能够攻击的所有坐标点的和,简而言之就是一个坐标点的两条对角线的和。
设两个数组d1[],d2[],用于保存两种方向下的对角线总和。(虚线部分标明了方向)。d1[]数组是通过d1[i+j] += board[i][j] 来算的,而 d2是这样:d2[i-j+n] += board[i][j]。
如果要求某个特定的坐标(i, j)的对角线和,那么就是d1[i+j] + d2[i-j+n] - board[i][j] ,之所以要减去board是因为每个坐标点的和都被重复算多了一次。
(2)判断攻击的方法
假设 bishop1 坐标为(i1, j1),bishop2 为(i2, j2),如果( i1 + j1),( i2 + j2) 同奇或同偶,那么就存在某一个坐标点同时被两个bishop 攻击!
所以要满足不存在某个坐标同时被两个bishop 攻击,就需要(i1 + j1) 和 (i2+j2) 处于一奇一偶的情况。那么奇数找一个最大值,偶数的话又找一个最大值,加起来就是两个bishop放置后能够获得的最大和了。
Time: 217ms Memory: 31400KB
1 #include <iostream> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <cstring> 5 using namespace std; 6 7 typedef long long LL; 8 const int maxn = 2000 + 2; 9 10 LL board[maxn][maxn]; 11 LL d1[maxn<<1], d2[maxn<<1]; 12 pair<int, int> ans[2]; 13 LL tmp[2]; 14 15 inline LL read() // 这个读入是避免TLE的关键 16 { 17 int x = 0, f = 1; 18 char ch = getchar(); 19 while (ch >= '0' && ch <= '9') 20 { 21 x = 10*x + ch-'0'; 22 ch = getchar(); 23 } 24 return (LL)(x * f); 25 } 26 27 int main() 28 { 29 int n, x1, x2, y1, y2; 30 while (scanf("%d", &n) != EOF) 31 { 32 memset(d1, 0, sizeof(d1)); 33 memset(d2, 0, sizeof(d2)); 34 getchar(); // 不能省!n之后有个空格= = 35 for (int i = 1; i <= n; i++) 36 { 37 for (int j = 1; j <= n; j++) 38 { 39 board[i][j] = read(); 40 d1[i+j] += board[i][j]; 41 d2[i-j+n] += board[i][j]; 42 } 43 } 44 tmp[0] = tmp[1] = -1; // 0也可以,但是后面要>=,防止多次被替换还是-1好,省时一点吧 45 for (int i = 1; i <= n; i++) 46 { 47 for (int j = 1; j <= n; j++) 48 { 49 LL val = d1[i+j] + d2[i-j+n] - board[i][j]; 50 if (val > tmp[(i+j)&1]) 51 { 52 tmp[(i+j)&1] = val; 53 ans[(i+j)&1].first = i; 54 ans[(i+j)&1].second = j; 55 } 56 } 57 } 58 printf("%lld\n", tmp[0] + tmp[1]); 59 printf("%d %d %d %d\n", ans[0].first, ans[0].second, ans[1].first, ans[1].second); 60 } 61 return 0; 62 }
接下来就是我的做法(大家可以忽略)
求对角线的和的时候,我是采取从第1行,第1列,最后1列出发来求得的,最后还是需要减去board[i][j],代码量好大,因为太多重复 = =
至于判断攻击,除了利用abs函数(恰好对角线),还利用了之前做的一条 cf370A 的 Rook, Bishop and King的做法啦—— 判断Bishop 步数。
毕竟自己写的,留下纪念吧 = =
真是又长又耗时啊~~
Time: 1122ms Memory: 125504KB
1 #include <iostream> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <cstring> 5 #include <cmath> 6 #include <algorithm> 7 using namespace std; 8 9 typedef long long LL; 10 const int maxn = 2000 + 2; 11 12 struct node 13 { 14 LL s; 15 int x, y; 16 bool operator < (const node& a) const 17 { 18 return s < a.s; 19 } 20 }num[maxn*maxn]; 21 22 LL board[maxn][maxn]; 23 LL sum[maxn][maxn]; 24 25 inline LL read() // 这个读入是避免TLE的关键 26 { 27 int x = 0, f = 1; 28 char ch = getchar(); 29 while (ch >= '0' && ch <= '9') 30 { 31 x = 10*x + ch-'0'; 32 ch = getchar(); 33 } 34 return (LL)(x * f); 35 } 36 37 int main() 38 { 39 int n; 40 while (scanf("%d", &n) != EOF) 41 { 42 getchar(); 43 for (int i = 1; i <= n; i++) 44 { 45 for (int j = 1; j <= n; j++) 46 board[i][j] = (LL)read(); 47 } 48 memset(sum, 0, sizeof(sum)); 49 // 第 1 行 50 int i = 1; 51 for (int j = 1; j <= n; j++) 52 { 53 LL ss = 0; 54 int ti = i; 55 int tj = j; // 右下 56 while (ti <= n && tj <= n) 57 { 58 ss += board[ti][tj]; 59 ti++; 60 tj++; 61 } 62 ti = i; 63 tj = j; 64 while (ti <= n && tj <= n) 65 { 66 sum[ti][tj] += ss; 67 ti++; 68 tj++; 69 } 70 ti = i; 71 tj = j; // 左下 72 ss = 0; 73 while (ti <= n && tj >= 1) 74 { 75 ss += board[ti][tj]; 76 ti++; 77 tj--; 78 } 79 ti = i; 80 tj = j; 81 while (ti <= n && tj >= 1) 82 { 83 sum[ti][tj] += ss; 84 ti++; 85 tj--; 86 } 87 } 88 // 第 1 列 89 int j = 1; 90 for (int i = 2; i <= n; i++) 91 { 92 LL ss = 0; 93 int ti = i; 94 int tj = j; 95 while (ti <= n && tj <= n) 96 { 97 ss += board[ti][tj]; 98 ti++; 99 tj++; 100 } 101 102 ti = i; 103 tj = j; 104 while (ti <= n && tj <= n) 105 { 106 sum[ti][tj] += ss; 107 ti++; 108 tj++; 109 } 110 } 111 j = n; 112 for (int i = 2; i <= n; i++) 113 { 114 LL ss = 0; 115 int ti = i; 116 int tj = j; 117 while (ti <= n && tj >= 1) 118 { 119 ss += board[ti][tj]; 120 ti++; 121 tj--; 122 } 123 ti = i; 124 tj = j; 125 while (ti <= n && tj >= 1) 126 { 127 sum[ti][tj] += ss; 128 ti++; 129 tj--; 130 } 131 } 132 int cnt = 0; 133 for (int i = 1; i <= n; i++) 134 { 135 for (int j = 1; j <= n; j++) 136 { 137 num[cnt].x = i; 138 num[cnt].y = j; 139 num[cnt++].s = sum[i][j] - board[i][j]; 140 } 141 } 142 int flag = 0; 143 LL maxsum; 144 int x1, y1, x2, y2; 145 sort(num, num+cnt); 146 for (int i = cnt-1; i >= 0 && !flag; i--) 147 { 148 for (int j = i-1; j >= 0 && !flag; j--) 149 { 150 int t1 = num[i].x + num[i].y; 151 int t2 = abs(num[j].x - num[j].y); 152 if ((t1 + t2) % 2 == 0) 153 continue; 154 if (abs(num[i].x-num[j].x) != abs(num[i].y - num[j].y)) 155 { 156 flag = 1; 157 x1 = num[i].x, x2 = num[j].x; 158 y1 = num[i].y, y2 = num[j].y; 159 maxsum = num[i].s + num[j].s; 160 break; 161 } 162 } 163 } 164 printf("%lld\n", maxsum); 165 printf("%d %d %d %d\n", x1, y1, x2, y2); 166 } 167 return 0; 168 }