CF1272E. Nearest Opposite Parity 题解 广度优先搜索

题目链接:http://codeforces.com/contest/1272/problem/E

题目大意:
有一个长度为n的数组 \(a\) ,数组坐标从 \(1\) 到 \(n\) 。
假设你现在处于数组中的某一个位置,我们假设这个坐标为 \(i\) ,那么:
如果 \(1 <= i-a[i]\) ,那么你可以从坐标 \(i\) 移动到坐标 \(i-a[i]\) 位置(花费一步);
如果 \(i+a[i]<=n\) ,那么你可以从坐标 \(i\) 移动到坐标 \(i+a[i]\) 位置(同样花费一步)。
对于从 \(1\) 到 \(n\) 的所有坐标 \(i\) 来说,你想要的知道你最少需要几步可以从坐标 \(i\) 移动到一个坐标\(j\) ,
且 \(a[i]\) 和 \(a[j]\) 具有不同的奇偶性(也就是说,如果 \(a[i]\) 是偶数,那么 \(a[j]\) 就是奇数;如果 \(a[i]\) 是奇数,那么 \(a[j]\) 就得是偶数)。

解题思路:
这个问题可以转换成图论模型,然后用广度优先搜索或者最短路算法(Dijkstra或者SPFA算法)进行求解。
这里因为可能有些同学没有学到最短路算法,并且用最短路算法解决广搜可以解决的问题有一点杀鸡用牛刀的感觉(今年CSP-J复赛第4题可以用广搜做然而我也用了最短路SPFA算法),所以我这里还是讲解广度优先搜索解决这个问题。
对于一个边权均为 \(1\) 的图来说,我们通过广度优先搜索得到的就是最短路。因为此时一开始入队列的都是距起点 \(0\) 的点,然后入队列的是距起点 \(1\) 的点,然后入队列的是距起点 \(2\) 的点,……(大家可以自己画图体会一下,此时的广度优先搜索就是一个层次遍历)

然后我们来看怎么使用广度优先搜索解决这个问题。

首先对于 \(a[i]\) 来说,

  • 如果 \(i-a[i] \ge 1\),则从 \(i-a[i]\) 向 \(i\) 连一条边;
  • 如果 \(i+a[i] \le n\),则从 \(i+a[i]\) 向 \(i\) 连一条边。

然后就可以开始我们的搜索了。
在搜索之前我们需要先开两个数组:

  • \(odd[i]\) 表示坐标为 \(i\) 的点到达最近的一个奇数点所需的最少步数;
  • \(even[i]\) 表示坐标为 \(i\) 的点到达最近的一个偶数点所需的最少步数。

我们先来求 \(odd\) 值,
我们从 \(1\) 到 \(n\) 遍历 \(i\) ,

  • 如果 \(a[i]\) 是奇数,则设 \(odd[i]\) 为 \(0\),同时将其加入队列;
  • 否则(\(a[i]\) 为偶数),设 \(odd[i]\) 为无穷大。

接下来每次从队列里面取出一个坐标 \(u\),然后遍历所有 \(u\) 可以到达的点 \(v\) ,如果 \(odd[v] > odd[u]+1\) ,则标记 \(odd[v] = odd[u]+1\) ,同时将 \(v\) 加入队列。

最终每个点的 \(odd\) 值即表示从坐标 \(i\) 出发最少需要花费多少步能够到达一个奇数值得点。

然后我们再来求 \(even\) 值(其实你们会发现是一样的道理),
我们从 \(1\) 到 \(n\) 遍历 \(i\) ,

  • 如果 \(a[i]\) 是偶数,则设 \(odd[i]\) 为 \(0\),同时将其加入队列;
  • 否则(\(a[i]\) 为奇数),设 \(odd[i]\) 为无穷大。

接下来每次从队列里面取出一个坐标 \(u\),然后遍历所有 \(u\) 可以到达的点 \(v\) ,如果 \(odd[v] > odd[u]+1\) ,则标记 \(odd[v] = odd[u]+1\) ,同时将 \(v\) 加入队列。

自此,我们就求解完了 \(odd\) 和 \(even\) 值。

然后我们从 \(1\) 到 \(n\) 遍历坐标 \(i\),

  • 如果 \(a[i]\) 是奇数,输出 \(even[i]\);
  • 如果 \(a[i]\) 是偶数,输出 \(odd[i]\)。

实现代码如下:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 200020;
int n, a[maxn], odd[maxn], even[maxn];
vector<int> g[maxn];
queue<int> que;
int main() {
    cin >> n;
    for (int i = 1; i <= n; i ++) cin >> a[i];
    for (int i = 1; i <= n; i ++) {
        if (i-a[i] >= 1) g[ i-a[i] ].push_back(i);
        if (i+a[i] <= n) g[ i+a[i] ].push_back(i);
    }
    // odd
    for (int i = 1; i <= n; i ++) {
        if (a[i]%2) {
            odd[i] = 0;
            que.push(i);
        }
        else odd[i] = -1;
    }
    while (!que.empty()) {
        int u = que.front();
        que.pop();
        int sz = g[u].size();
        for (int i = 0; i < sz; i ++) {
            int v = g[u][i];
            if (odd[v] == -1 || odd[v] > odd[u] + 1) {
                odd[v] = odd[u] + 1;
                que.push(v);
            }
        }
    }
    // even
    for (int i = 1; i <= n; i ++) {
        if (a[i] % 2 == 0) {
            even[i] = 0;
            que.push(i);
        }
        else even[i] = -1;
    }
    while (!que.empty()) {
        int u = que.front();
        que.pop();
        int sz = g[u].size();
        for (int i = 0; i < sz; i ++) {
            int v = g[u][i];
            if (even[v] == -1 || even[v] > even[u] + 1) {
                even[v] = even[u] + 1;
                que.push(v);
            }
        }
    }
    for (int i = 1; i <= n; i ++) {
        if (i > 1) putchar(' ');
        if (a[i] % 2) cout << even[i];
        else cout << odd[i];
    }
    cout << endl;
    return 0;
}
上一篇:Exercise 25 - even more practice


下一篇:leetcode1217