小美和大富翁

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

思路

关键观察

每一轮小美必须使用跳跃 1、2、3、4 的四张卡牌,总共前进 个城市。因此:

  • 如果 不是 10 的倍数,则无法恰好到达第 个城市,输出
  • 总共需要 轮,每轮从当前位置前进恰好 10 格。

每轮的选择

在一轮中,小美选择四张卡牌的使用顺序(即 的一个排列),依次跳跃,途中经过 4 个城市。不同的排列会经过不同的中间城市,因此获得的金币总数也不同。

共有 种排列,每种排列对应一组经过的城市。例如排列 表示先跳 3 格、再跳 1 格、再跳 4 格、再跳 2 格,经过的城市偏移量为

贪心策略

由于金币数必须始终 ,拥有更多金币意味着更大的灵活性——当前金币越多,未来能选择的排列只会更多,不会更少。因此贪心策略是最优的:每轮在所有可行的排列中,选择金币收益最大的

具体做法:

  1. 枚举 24 种排列,对每种排列模拟 4 步跳跃。
  2. 检查每步跳跃后金币是否 (可行性检查)。
  3. 在所有可行排列中取金币收益最大值。
  4. 如果某一轮没有任何可行排列,输出

复杂度分析

  • 时间复杂度
  • 空间复杂度

代码

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

int main(){
    int n;
    scanf("%d", &n);
    vector<int> a(n + 1);
    for(int i = 1; i <= n; i++) scanf("%d", &a[i]);

    if(n % 10 != 0){
        printf("-1\n");
        return 0;
    }

    // 预计算 24 种排列对应的累计偏移
    int perms[24][4];
    int cnt = 0;
    int cards[] = {1, 2, 3, 4};
    do {
        int s = 0;
        for(int i = 0; i < 4; i++){
            s += cards[i];
            perms[cnt][i] = s;
        }
        cnt++;
    } while(next_permutation(cards, cards + 4));

    long long coins = 0;
    int rounds = n / 10;

    for(int r = 0; r < rounds; r++){
        int base = r * 10;
        long long best = -1;
        bool found = false;

        for(int p = 0; p < 24; p++){
            long long running = coins;
            bool valid = true;
            long long gain = 0;
            for(int i = 0; i < 4; i++){
                int city = base + perms[p][i];
                running += a[city];
                gain += a[city];
                if(running < 0){
                    valid = false;
                    break;
                }
            }
            if(valid && (!found || gain > best)){
                best = gain;
                found = true;
            }
        }

        if(!found){
            printf("-1\n");
            return 0;
        }
        coins += best;
    }

    printf("%lld\n", coins);
    return 0;
}
import java.util.*;

public class Main {
    static int[][] perms = new int[24][4];
    static int permCnt = 0;

    static void genPerms(int[] arr, int l) {
        if (l == arr.length) {
            int s = 0;
            for (int i = 0; i < 4; i++) {
                s += arr[i];
                perms[permCnt][i] = s;
            }
            permCnt++;
            return;
        }
        for (int i = l; i < arr.length; i++) {
            int tmp = arr[l]; arr[l] = arr[i]; arr[i] = tmp;
            genPerms(arr, l + 1);
            tmp = arr[l]; arr[l] = arr[i]; arr[i] = tmp;
        }
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] a = new int[n + 1];
        for (int i = 1; i <= n; i++) a[i] = sc.nextInt();

        if (n % 10 != 0) {
            System.out.println(-1);
            return;
        }

        genPerms(new int[]{1, 2, 3, 4}, 0);

        long coins = 0;
        int rounds = n / 10;

        for (int r = 0; r < rounds; r++) {
            int base = r * 10;
            long best = Long.MIN_VALUE;
            boolean found = false;

            for (int p = 0; p < 24; p++) {
                long running = coins;
                boolean valid = true;
                long gain = 0;
                for (int i = 0; i < 4; i++) {
                    int city = base + perms[p][i];
                    running += a[city];
                    gain += a[city];
                    if (running < 0) {
                        valid = false;
                        break;
                    }
                }
                if (valid) {
                    if (!found || gain > best) {
                        best = gain;
                        found = true;
                    }
                }
            }

            if (!found) {
                System.out.println(-1);
                return;
            }
            coins += best;
        }

        System.out.println(coins);
    }
}