变幻莫测

[题目链接](https://www.nowcoder.com/practice/a704b3aa55ce48dfaf854bf7c7b3c989)

思路

题意是:给两个整数 ,每次可以做两种操作中的一种:

  • 操作一(交换):把 变成
  • 操作二(变换):把 变成

问最少操作几次能让 ,做不到就输出

先观察操作二的性质。做一次操作二后,新的一对数的和是 ,差是 。如果连续做两次操作二,,相当于同时乘以 2。这说明操作二会让数值快速增长,不可能无限做下去,搜索空间是有限的。

什么时候 ?做完操作二后 意味着 。所以关键是:能不能通过若干步操作把 (或交换后的 )变成 0?

由于数据范围很小(),而且操作二会让数值指数级增长,所以从起始状态出发,BFS 搜索所有可达状态即可。每个状态是一个 二元组,每步有两种转移。数值增长很快,几十步内要么找到答案,要么数值爆炸,因此加一个合理的边界限制就行。

具体做法:

  1. 作为初始状态放入队列。
  2. 每次取出队首,分别尝试两种操作生成新状态。
  3. 如果新状态满足两个分量相等,返回当前步数
  4. 对操作二生成的状态,限制绝对值不超过一个阈值(比如 ),避免无意义地膨胀。
  5. 用哈希表记录已访问状态,防止重复。
  6. 搜索结束仍未找到则输出

拿样例验证一下:,三步搞定。而 怎么变都无法让两数相等,输出

代码

#include <bits/stdc++.h>
using namespace std;

int main() {
    int x, y;
    cin >> x >> y;
    if (x == y) {
        cout << 0 << endl;
        return 0;
    }
    map<pair<int,int>, int> dist;
    queue<pair<int,int>> q;
    dist[{x, y}] = 0;
    q.push({x, y});
    while (!q.empty()) {
        auto [a, b] = q.front();
        q.pop();
        int d = dist[{a, b}];
        if (d > 40) break;
        // 操作一:交换
        pair<int,int> s1 = {b, a};
        if (s1.first == s1.second) {
            cout << d + 1 << endl;
            return 0;
        }
        if (!dist.count(s1)) {
            dist[s1] = d + 1;
            q.push(s1);
        }
        // 操作二:(a+b, a-b)
        pair<int,int> s2 = {a + b, a - b};
        if (s2.first == s2.second) {
            cout << d + 1 << endl;
            return 0;
        }
        if (abs(s2.first) <= 100000 && abs(s2.second) <= 100000 && !dist.count(s2)) {
            dist[s2] = d + 1;
            q.push(s2);
        }
    }
    cout << -1 << endl;
    return 0;
}
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int x = sc.nextInt(), y = sc.nextInt();
        if (x == y) {
            System.out.println(0);
            return;
        }
        Map<Long, Integer> dist = new HashMap<>();
        Queue<int[]> q = new LinkedList<>();
        dist.put(encode(x, y), 0);
        q.add(new int[]{x, y});
        while (!q.isEmpty()) {
            int[] cur = q.poll();
            int a = cur[0], b = cur[1];
            int d = dist.get(encode(a, b));
            if (d > 40) break;
            // 操作一:交换
            if (b == a) { System.out.println(d); return; }
            long k1 = encode(b, a);
            if (b == a) { System.out.println(d + 1); return; }
            if (!dist.containsKey(k1)) {
                dist.put(k1, d + 1);
                q.add(new int[]{b, a});
            }
            // 操作二:(a+b, a-b)
            int na = a + b, nb = a - b;
            if (na == nb) { System.out.println(d + 1); return; }
            if (Math.abs(na) <= 100000 && Math.abs(nb) <= 100000) {
                long k2 = encode(na, nb);
                if (!dist.containsKey(k2)) {
                    dist.put(k2, d + 1);
                    q.add(new int[]{na, nb});
                }
            }
        }
        System.out.println(-1);
    }

    static long encode(int a, int b) {
        return (long)(a + 200000) * 400001 + (b + 200000);
    }
}
from collections import deque

def solve():
    x, y = map(int, input().split())
    if x == y:
        print(0)
        return
    dist = {(x, y): 0}
    q = deque([(x, y)])
    while q:
        a, b = q.popleft()
        d = dist[(a, b)]
        if d > 40:
            break
        # 操作一:交换
        if (b, a) not in dist:
            if b == a:
                print(d + 1)
                return
            dist[(b, a)] = d + 1
            q.append((b, a))
        # 操作二:(a+b, a-b)
        na, nb = a + b, a - b
        if na == nb:
            print(d + 1)
            return
        if abs(na) <= 100000 and abs(nb) <= 100000 and (na, nb) not in dist:
            dist[(na, nb)] = d + 1
            q.append((na, nb))
    print(-1)

solve()
const readline = require('readline');
const rl = readline.createInterface({ input: process.stdin });
rl.on('line', (line) => {
    const [x, y] = line.trim().split(' ').map(Number);
    if (x === y) {
        console.log(0);
        process.exit();
    }
    const dist = new Map();
    const q = [[x, y]];
    const key = (a, b) => a * 400001 + b;
    dist.set(key(x, y), 0);
    let head = 0;
    while (head < q.length) {
        const [a, b] = q[head++];
        const d = dist.get(key(a, b));
        if (d > 40) break;
        // 操作一:交换
        const k1 = key(b, a);
        if (b === a) { console.log(d + 1); process.exit(); }
        if (!dist.has(k1)) {
            dist.set(k1, d + 1);
            q.push([b, a]);
        }
        // 操作二:(a+b, a-b)
        const na = a + b, nb = a - b;
        if (na === nb) { console.log(d + 1); process.exit(); }
        if (Math.abs(na) <= 100000 && Math.abs(nb) <= 100000) {
            const k2 = key(na, nb);
            if (!dist.has(k2)) {
                dist.set(k2, d + 1);
                q.push([na, nb]);
            }
        }
    }
    console.log(-1);
    process.exit();
});

复杂度分析

  • 时间复杂度:由于操作二会让数值指数级增长,可达状态数是有限的(在边界限制下为常数级别),BFS 的时间复杂度为 ,其中 是可达状态数,实际非常小。
  • 空间复杂度,用于存储已访问状态的哈希表和队列。