Contest 2050 and Codeforces Round #718 游记

难得有一场比赛大家全都来打了!

然而却是越做越糊涂,D 题开始搞错 n,mn,m 后来又分析错时间,最后绝望极了,没能坚持到最后,放弃治疗。第二天发现其实挺简单的。

A

简单题。一遍过。

B

血的教训!清空不能一直滥用 memset, 不然这种多测保证积的和不超过几几几的会 T 掉。

C

简单题。一遍过。

D

给定一张网格图,求每个点走 kk 次后恰好回来所经过的最小边权。

恰好回来的这种问题一看就想到分层图,然后一算时间是 nmk2nmk^2, 看到 kk 才二十以为能过,就无脑写了一波分层图 Dijkstra, 然后一开始搞错了 nnmm, Wrong Answer on pretest 4, 修改后又 T 在了 pretest 5, 还傻乎乎的以为是小问题,然后交了两次 T 才自己造数据。

还瞎搞了一些无关紧要的压低常数做法,结果到好久之后才发现有 kk 张图,时间应该是 nmk3nmk^3 的,根本就是渐进复杂度错了。然后就绝望等死了没去重新想正解,转而放弃治疗了。

其实正解还要简单。

有一个很显然的性质,那就是一定是跑 k2\frac{k}{2} 步后原路返回的,反证法易证。那么只需要看一个走 k2\frac{k}{2} 步最少跨越多少边权了,随便 dp 或者记忆化搜索都可以。

最后还发现,原来先前写的代码不仅 n,mn,m 搞错,循环的顺序也反了……

代码。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define int long long
const int N = 505, K = 25, INF = 0x3f3f3f3f3f3f3f3fll, D = 1, U = 2, L = 3, R = 4;
int n, m, k, f[N][N][K], to[5][N][N], ans[N][N];
int dx[5] = {0, 1, -1, 0, 0},
    dy[5] = {0, 0, 0, -1, 1};
int dfs(int x, int y, int k) {
    if(k == 0) return 0;
    if(f[x][y][k] != -1) return f[x][y][k];
    f[x][y][k] = INF;
    for(int i = 1; i <= 4; i++) {
        int xx = x + dx[i], yy = y + dy[i];
        if(xx < 1 || xx > n || yy < 1 || yy > m) continue;
        f[x][y][k] = std::min(f[x][y][k], dfs(xx, yy, k-1) + to[i][x][y]);
    }
    return f[x][y][k];
}
signed main() {
    scanf("%lld%lld%lld", &n, &m, &k);
    for(int i = 1; i <= n; i++)
        for(int j = 1; j < m; j++) scanf("%lld", &to[R][i][j]), to[L][i][j+1] = to[R][i][j];
    for(int i = 1; i < n; i++)
        for(int j = 1; j <= m; j++) scanf("%lld", &to[D][i][j]), to[U][i+1][j] = to[D][i][j];
    memset(f, -1, sizeof f);
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++) 
            dfs(i, j, k/2);
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= m; j++) printf("%lld ", (k & 1) ? -1 : f[i][j][k/2]*2);
        puts("");
    }
    return 0;
}

虽说比赛打得不怎么样,但别灰心啊,继续加油,总会有突破的。