《国际象棋跳跃问题》《小U走排列问题》

131 阅读4分钟

《国际象棋跳跃问题》

题面

image.png

问题理解

题目要求计算由起点 (x1, y1) 到终点 (x2, y2) 所需的最少步数。移动方式有两种:

  1. 象的跳跃:可以移动到 (x + k, y + k)(x + k, y - k),其中 k 是任意整数。
  2. 马的跳跃:可以移动到 (x + a, y + b),其中 |a| + |b| = 31 ≤ |a|, |b| ≤ 2

解题思路

  1. 象的跳跃

    • 象的跳跃方式是沿着对角线移动。如果起点和终点的横纵坐标差值相等(即 abs(x2 - x1) == abs(y2 - y1)),那么可以直接一步到达。
  2. 马的跳跃

    • 马的跳跃方式是沿着 |a| + |b| = 3 的路径移动。我们需要检查是否存在一个 (a, b) 使得 (x1 + a, y1 + b) == (x2, y2)
  3. 组合跳跃

    • 如果直接一步无法到达,我们需要考虑组合跳跃。即先使用马的跳跃方式移动到一个中间点,然后再使用象的跳跃方式到达终点。
  4. 特殊情况

    • 如果起点和终点的横纵坐标和的奇偶性不同(即 (abs(x1) + abs(y1)) % 2 != (abs(x2) + abs(y2)) % 2),则无法到达,因为象的跳跃总是改变奇偶性。

具体实现

int solution(int x1, int y1, int x2, int y2) {
    // write code here
    // 象的跳跃一步到达
    if(abs(y2 - y1) == abs(x2 - x1)){
        return 1;
    }
    // 马的跳跃一步到达
    for(int a=-2;a<=2;a++){
        for(int b=-2;b<=2;b++){
            if(abs(a) + abs(b) == 3 && 1<=abs(a) && abs(a)<=2 && 1<=abs(b) && abs(b)<=2){
                if(x1+a == x2 && y1+b == y2){
                    return 1;
                }
            }
        }
    }
    // 马 + 象的跳跃两步到达
    for(int a=-2;a<=2;a++){
        for(int b=-2;b<=2;b++){
            if(abs(a) + abs(b) == 3 && 1<=abs(a) && abs(a)<=2 && 1<=abs(b) && abs(b)<=2){
                if(abs(y2 - (y1+b)) == abs(x2 - (x1+a))){
                    return 2;
                }
            }
        }
    }
    // 象的跳跃两步到达 或者 三步到达
    return (abs(x1)+abs(y1))%2 == (abs(x2)+abs(y2))%2 ? 2 : 3;
}

《小U走排列问题》

题面

image.png

问题理解

题目要求计算在所有不同的访问顺序中,小U由原点出发依次访问这些点所走过的路径总和。每种顺序对应的路径长度等于她由原点出发依次访问这些点所走的距离之和。

解题思路

  1. 路径长度的计算

    • 对于每个点 aia_i,我们需要计算它在所有排列中被访问的次数以及它对总路径长度的贡献。
    • 假设点 aia_i 在某个排列中被访问的次数是 k,那么它对总路径长度的贡献是 k×aiai1k \times |a_i - a_{i-1}|
  2. 排列的考虑

    • 所有不同的访问顺序有 (n1)!(n-1)! 种。
    • 对于每个点 aia_i ,它在所有排列中被访问的次数是 (n1)!(n-1)! 次。
  3. 路径总和的计算

    • 对于每个点 aia_i ,我们需要计算它在所有排列中被访问的次数以及它对总路径长度的贡献。
    • 对于每个点 aia_i ,我们需要计算它位于第一个位置时和原点之间对总路径长度的贡献

代码实现思路

  1. 排序

    • 首先对数组 aa 进行排序,这样可以简化路径长度的计算。
  2. 计算路径总和

    • 对于每个点 aia_i,我们需要计算它在所有排列中被访问的次数以及它对总路径长度的贡献。
  3. 阶乘计算

    • 使用阶乘函数计算 n!n!,以便在计算路径总和时使用。

具体实现

int mod = 1e9+7;
// 计算阶乘
int factorial(int n) {
    vector<int> fact(n + 1, 1);
    for (int i = 2; i <= n; ++i) {
        fact[i] = (1LL * fact[i - 1] * i) % mod;
    }
    return fact[n];
}

int solution(int n, vector<int> a) {
    // write code here
    int ret=0;
    long long sum1=0, sum2=0, sum3=0;
    sort(a.begin(), a.end());
    // sum2是数组元素总和
    for(int i=0;i<n;i++){
        sum2 += a[i];
    }
    // sum3是不含当前元素的数组前缀和,sum1是所有元素对之间的路径长度和
    for(int i=0;i<n;i++){
        sum1 += sum2 - sum3 - a[i] - a[i] * (n-i-1) + a[i] * i - sum3;
        sum3 += a[i];
    }
    // 计算所有元素对之间的路径长度
    ret = (ret + sum1 * factorial(n-1)) % mod;
    // 计算每个元素为第一个元素时到原点的路径长度
    ret = (ret + sum2 * factorial(n-1)) % mod;
    return ret;
}