[杂项/无聊向]《美食大战老鼠》裸强卡最优策略搜索代码(非游戏玩家勿点)

最近常登《美食大战老鼠》这款沙雕游戏,不得不赞叹道里面的卡片强化系统真的****。为了最大化用有限卡裸上出高星卡的成功概率,我就写了这么个大暴力程序。

算法上,先用了个不能再沙雕的裸搜索(用哈希存状态),然后上个无聊的大 dp 就完事儿了。

用下面的代码实际操作了几次后感觉最优策略似乎有规律可循,所以应该还有提速空间。代码本身也有一些可优化的地方,不过懒得改了。

基本使用方式:先输入 \(n\) 表示你已有的卡片数量,再输入 \(n\) 个数表示 \(n\) 张卡片的星级(顺序可任意),最后输入一个数表示你希望强化出的卡片星级。每一步程序都会输出最终成功概率以及当前的最优决策,按要求执行反馈即可。

注意事项:

  1. 此代码未对任何非法操作做判断,所以请尽量保证输入合法(比如,不要出现希望强化出的卡片星级低于已有卡片的情况)
  2. 由于算法过于暴力,所以卡片数量不宜太多(最好不超过 \(50\) 张)
  3. 下面的概率表默认强化用卡为高耗能卡(你应该明白是什么意思)
  4. 强化默认所使用的是同一种四叶草(对应的程序语句为const double mul = 1.0;mul表示四叶草对应的概率倍数,例如:1.0为不使用四叶草,使用 1 级四叶草则将1.0改为1.2,以此类推)
  5. 由于未找到 15 星及以上强化对应的概率,因此下面的代码只适用于 15 级以下的强化(我并不知道强化的基本概率的计算公式,所以只能打表)
  6. 再次强调,是裸上(即用单卡强化,因为我平时就单上,懒得写复杂情况了
  7. 可能有bug
#include<bits/stdc++.h>

using namespace std;

const int N = 1000000;
const int M = 16;
const int mod = 998244353;
const double mul = 1.0;
const double eps = 1e-8;

int n, all, goal;
double p[M][M], f[N];
bool visit[N];
map<int, int> hash_t;
pair<int, int> best[N];

void init() {
  p[0][0] = 1;
  p[1][1] = 1;
  p[2][2] = 0.9683;
  p[3][3] = 0.6858;
  p[4][4] = 0.495;
  p[5][5] = 0.3958;
  p[6][6] = 0.3192;
  p[7][7] = 0.2642;
  p[8][8] = 0.22;
  p[9][9] = 0.135;
  p[10][10] = 0.125;
  p[11][11] = 0.116;
  p[12][12] = 0.107;
  p[13][13] = 0.101;
  p[14][14] = 0.095;

  p[1][0] = 0.88;
  p[2][1] = 0.792;
  p[3][2] = 0.55;
  p[4][3] = 0.4033;
  p[5][4] = 0.33;
  p[6][5] = 0.264;
  p[7][6] = 0.212;
  p[8][7] = 0.132;
  p[9][8] = 0.045;
  p[10][9] = 0.044;
  p[11][10] = 0.043;
  p[12][11] = 0.0398;
  p[13][12] = 0.0367;
  p[14][13] = 0.0336;

  p[2][0] = 0.6083;
  p[3][1] = 0.4292;
  p[4][2] = 0.2417;
  p[5][3] = 0.2008;
  p[6][4] = 0.132;
  p[7][5] = 0.106;
  p[8][6] = 0.06;
  p[9][7] = 0.022;
  p[10][8] = 0.018;
  p[11][9] = 0.017;
  p[12][10] = 0.0156;
  p[13][11] = 0.0141;
  p[14][12] = 0.0126;

  for (int i = 0; i < 15; ++i) {
    p[i][i] *= mul;
    p[i][i] = min(p[i][i], 1.); 
  }
  for (int i = 1; i < 15; ++i) {
    p[i][i - 1] *= mul;
    p[i][i - 1] = min(p[i][i - 1], 1.);
  }
  for (int i = 2; i < 15; ++i) {
    p[i][i - 2] *= mul;
    p[i][i - 2] = min(p[i][i - 2], 1.);
  }
}

pair<bool, int> decode(vector<int> info) {
  bool exist = true;
  int base = 0;
  for (auto x : info) {
    base = (base * 233ll + x) % mod;
  }
  if (!hash_t.count(base)) {
    exist = false;
    hash_t[base] = ++all;
    if (info[goal]) {
      visit[all] = true;
      f[all] = 1;
    }
  }
  return make_pair(exist, hash_t[base]);
}

void strengthen(int i, int j, vector<int>& number, bool success = true) {
  --number[i];
  --number[j];
  if (success) {
    ++number[i + 1];
  } else {
    ++number[i - (i > 5)];
  }
}

int count(vector<int> number) {
  int result = 0;
  for (auto x : number) {
    result += x;
  }
  return result;
}

void dfs(vector<int> number) {
  bool exist = decode(number).first;
  if (count(number) != 1 && exist == false) {
    for (int i = 0; i < 15; ++i) {
      for (int j = max(0, i - 2); j < i; ++j) {
        if (number[i] && number[j]) {
          vector<int> next_array = number;
          strengthen(i, j, next_array);
          dfs(next_array);
          next_array = number;
          strengthen(i, j, next_array, false);
          dfs(next_array);
        }
      }
      if (number[i] >= 2) {
        vector<int> next_array = number;
        strengthen(i, i, next_array);
        dfs(next_array);
        next_array = number;
        strengthen(i, i, next_array, false);
        dfs(next_array);
      }
    }
  }
}

double dp(vector<int> number) {
  int id = decode(number).second;
  if (visit[id]) {
    return f[id];
  }
  visit[id] = true;
  if (count(number) == 1) {
    return f[id] = 0;
  } else {
    double& answer = f[id]; 
    for (int i = 0; i < 15; ++i) {
      for (int j = max(0, i - 2); j < i; ++j) {
        if (number[i] && number[j]) {
          vector<int> array1 = number, array0 = number;
          strengthen(i, j, array1);
          strengthen(i, j, array0, false);
          double foo = dp(array1) * p[i][j] + dp(array0) * (1 - p[i][j]);
          if (foo > answer) {
            best[id] = make_pair(i, j);
            answer = foo;
          }
        }
      }
      if (number[i] >= 2) {
        vector<int> array1 = number, array0 = number;
        strengthen(i, i, array1);
        strengthen(i, i, array0, false);
        double foo = dp(array1) * p[i][i] + dp(array0) * (1 - p[i][i]);
        if (foo > answer) {
          best[id] = make_pair(i, i);
          answer = foo;
        }
      }
    }
    return answer;
  }
}

int main() {
  init();
  cin >> n;
  vector<int> number(16);
  for (int i = 1; i <= n; ++i) {
    int x;
    cin >> x;
    ++number[x];
  }
  cin >> goal;
  dfs(number);
  //freopen("log.txt", "w", stdout);
  assert(all < N);
  //cerr << all << '\n';
  cout << "probability: " << setprecision(10) << dp(number) << '\n';
  if (dp(number) > eps) {
    while (1) {
      int id = decode(number).second;
      cout << "the best choice is: strengthen " << best[id].first << " with " << best[id].second << '.' << '\n';
      cout << "have you succeeded? (input 1 for success and 0 for failure)" << '\n';
      string inf;
      cin >> inf;
      strengthen(best[id].first, best[id].second, number, inf == "1");
      id = decode(number).second;
      if (f[id] == 1) {
        cout << "congratulation!" << '\n';
        break;
      } else if (f[id] < eps) {
        cout << "sorry, it's impossible now." << '\n';
        break; 
      } else {
        cout << "probability now: " << setprecision(10) << f[id] << '\n';
      }
    }
  } else {
    cout << "it's impossible." << '\n';
  }
  cout << "done." << '\n';
  return 0;
}

猜你喜欢

转载自www.cnblogs.com/ImagineC/p/12314105.html
今日推荐