Codeforces Round #605 (Div. 3) E. Nearest Opposite Parity

题目链接

题意:

N 个数, 每个数a[i], 从 i 这个位置 只可以走到 i + a[i], i - a[i], 前提是走到的位置在 1 - n 之间, 问从 i 这个位置最少走几步走到j,a[i] 和 a[j] 的奇偶不一样

思路:

我一开始的思路就是用记忆化搜索, f 数组代表当前位 i 的答案, 如果搜到了 j 这个位置 且 j 这个位置的 f[j] 有值, 那么就直接返回就可以了, 但是这个方法错了,我也不知道错在了什么地方。

第二种方法。
把一步可以到达的先算出来, 放入队列中,
然后把 i j可达的连一条边, 且这条边是反向边。
然后我们不断从队列中取顶点出来,
假设顶点 u ,可达点为 v1, v2, v3, 由于这个是反向边,所以其实是 v1 走向 u,
判断 a[u] and a[v1] 他们的奇偶是不是不一样,如果不一样, 且 f[v1] == -1, then f[v1] = f[u] + 1; q.push(v1);

这个反向边就建的比较巧妙。 还有用到了队列。 从一步就可以走到的进行正着推下去。主要是反向变得便利。

反思:

我写这个题的时候, 直接就想到了dfs ,然后再加上 记忆化。 多么好的想法, 但是写出来的时候却不能过。

正着推 加上反向边, 真的是 nb 啊。
下次多想想。

#include
using namespace std;
const int N = 2e5+100;
int a[N],n,ans[N];
vector<int>f[N];
queue<int>q;
void dbg() {cout << endl;}
template<typename T, typename... A> void dbg(T a, A... x) {cout << a << ' '; dbg(x...);}
#define logs(x...) {cout << #x << " -> "; dbg(x);}
int main(){
    scanf("%d",&n);
    memset(ans, -1, sizeof ans);
    while(!q.empty()) q.pop();
    for (int i = 1; i <= n; ++i)
        scanf("%d",&a[i]);

    for (int i = 1; i <= n; ++i){
        if (i - a[i] > 0){
            f[i-a[i]].push_back(i);
            if (a[i-a[i]] % 2 != a[i] % 2) ans[i] = 1;
        }
        if (i + a[i] < n+1){
            f[i+a[i]].push_back(i);
            if (a[i+a[i]] % 2 != a[i] % 2) ans[i] = 1;
        }
        if (ans[i] == 1) q.push(i);
    }
    while(!q.empty()){
        int u = q.front(); q.pop();
        for (auto it: f[u]){
            if (ans[it] == -1 && a[it] % 2 == a[u] % 2){
                ans[it] = ans[u] + 1;
                q.push(it);
            }
        }
    }
    for (int i = 1; i <= n; ++i)
        printf("%d ", ans[i]);
    return 0;
}

你可能感兴趣的:(cf题解)