大家好啊,今天我来发一篇正经的题解。
传送门:
这道题虽然作为普及组第三题,但是其实并不算太难。只不过,咳,喜欢暴力的就不行了。
看一下数据规模:
对于 的数据,;
对于 的数据,。
要是暴力的话,一是九亿的数组跟不上,二是的时间复杂度承受不了。
不过,这里还是浅浅的、弱弱的给一个的代码吧:
- #include
- using namespace std;
- int a[105][105];
- int main() {
- int n, x, y, m = 1; cin >> n >> x >> y;
- for (int i = 0; i < n / 2 + 1; ++i) {
- for (int j = i; j < n - i; ++j)a[i][j] = m++;
- for (int j = i + 1; j < n - i; ++j)a[j][n - i - 1] = m++;
- for (int j = n - i - 2; j > i; --j)a[n - i - 1][j] = m++;
- for (int j = n - i - 1; j > i; --j)a[j][i] = m++;
- }
- cout << a[x - 1][y - 1] << endl;
- }
所以,就只有另求它方。
这道题可以看成一道递归,定义函数 ,每次的规律如下:
如果在最外层,设n为边长,那么就试着推算:
在上面:
在左边:
在右边:
在下面:
如果不是,那就往里面一层推:
也就是说,这种算法最多需要推到这个矩阵的层数次,故时间复杂度为。
代码:
- #include
- using namespace std;
- int f(int n, int i, int j) {
- if (i == 1)return j;
- if (j == n)return n + i - 1;
- if (i == n)return 3 * n - 2 - j + 1;
- if (j == 1)return 4 * n - 4 - i + 2;
- return f(n - 2, i - 1, j - 1) + 4 * (n - 1);
- }
- int main() {
- int n, i, j; cin >> n >> i >> j;
- cout << f(n, i, j) << endl;
- }
我:但是,我依然对不满意,想要把它变成。
观众:你可好,想代码想上天了!这题用根本做不出来!
我:不过,我们有没有办法直接确定这个数在哪一环与这一环的第一个数或是上一环的最后一个数是多少吗?
观众:。。。
……
经过漫长的推导,我算出来了!
以上是个段子,接下来正式开始分析。
要想求属于第几环相对简单,就是看i和j分别与边缘的距离。设层数为,则表达式简化为:
但是,我们实际要用判断语句,这样是为了求出是上下左右四个方位中的哪一个。
而想求出这一环的第一个数,就只能暴算了。
因为上一环的最后一个数就是在这一层外每一层周长之和,所以设:
这么说的话,我们就可以按前文所说的分上下左右四种情况去算了。
好了,这里思路都有了,那就写吧!
代码:
- #include
- using namespace std;
- int main() {
- int n, i, j, f, flag, m, ans, c; cin >> n >> i >> j;
- if (min(i, j) < (n + 1) - max(i, j)) {
- if (i < j)flag = 1;
- else flag = 4;
- }
- else {
- if (i > j)flag = 3;
- else flag = 2;
- }
- f = min(min(i, j), (n + 1) - max(i, j));
- m = 4 * (f - 1) * (n - f + 1);
- c = (n - 1) - 2 * (f - 1) + 1;
- i -= f - 1, j -= f - 1;
- if(flag == 1)ans = m + j;
- else if(flag == 2)ans = m + c + i - 1;
- else if(flag == 3)ans = m + 3 * c - j - 1;
- else ans = m + 4 * c - i - 2;
- cout << ans << endl;
- return 0;
- }
好了,今天发了一篇正经的题解,自认为已经很可了。拜拜!