[20052006-ptz] Decoding Martian Messages

每日一练做到的, 做不来.

Decoding Martian Messages

算法

动态规划, 字符串.

思路

首先可以看出这是一道动态规划的题目.

这道题要求输出最终的字符串而非答案, 我们可以考虑先计算出答案数组再反推出最终的字符串.

先来考虑动态规划. 我们令 \(f_{i, j}\) 表示枚举到答案的第 \(i\) 位, 当前位填的是给定单词集合 \(\mathbb{D}\) 中的第 \(j\) 个字符串的"最后一位"的最大概率. 注意, 这里的"最后一位"在 \(i < k\) 时实际上是第 \(j\) 个字符串的第 \(i\) 位而并非真正的最后一位.

那么有转移方程:

\[f_{i, j} = \begin{cases} f_{i - 1, j} \times p_{i, s} & j < i \\ \max(f_{i - 1, k} \times p_{i, s}) & j \ge i, \textrm{check}(j, k) \ is \ legal \end{cases} \]

其中 \(p_{i, s}\) 表示在第 \(i\) 位填字符 \(s\) 的概率, 已经在输入中给出. 其中 \(\textrm{check}(j, k)\) 指的是单词集合中两字符串的关系是否如下图所示.

.png

只有这样才能够合法地转移, 同时 \(\textrm{check}\) 可以 \(\mathcal{O}(d^2 k)\) 的复杂度内预处理出.

这样, 我们成功地处理出了 \(f\) 数组, 现在来思考如何推出答案.

假设 \(f_{n, pos}\) 为最终答案, 那么根据定义, 答案的最后一位即为第 \(pos\) 个字符串的最后一位.

再来倒序枚举每一位, 考虑什么时候第 \(n - 1\) 位合法? 枚举 \(\mathbb{D}\) 中每一个字符串, 首先 \(\textrm{check}\) 函数需要合法, 其次 \(f_{n - 1, k} \times p_{n, s} = f_{n, pos}\). 以此类推, 我们动态地维护一个 \(now\), 表示当前位是 \(\mathbb{D}\) 中第 \(now\) 个字符串的最后一位, 转移即可.

复制代码
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
#include "iostream" using namespace std; constexpr int N = 2e2 + 10, M = 1e3 + 10; int d, k, t, n, flag[N][N]; char s[N][30], ans[M]; long double p[M][30], f[M][N]; void init() { cin >> d >> k >> t; for (int i = 1; i <= d; ++i) cin >> (s[i] + 1), f[0][i] = 1; for (int i = 1; i <= d; ++i) for (int j = 1; j <= d; ++j) { int t = 1; for (int l = 1; l ^ k; ++l) if (s[i][l] != s[j][l + 1]) { t = 0; break; } flag[i][j] = t; } cin >> n; for (int i = 1; i <= n; ++i) for (int j = 0; j ^ t; ++j) cin >> p[i][j]; } void calculate() { for (int i = 1; i <= n; ++i) for (int j = 1; j <= d; ++j) { int nw = s[j][min(k, i)] - 'a'; if (i <= k) { f[i][j] = f[i - 1][j] * p[i][nw]; continue; } for (int l = 1; l <= d; ++l) if (flag[j][l]) f[i][j] = max(f[i][j], f[i - 1][l] * p[i][nw]); } long double mx = 0; for (int i = 1; i <= d; ++i) mx = max(mx, f[n][i]); if (mx == 0) return cout << "---", void(); for (int i = 1; i <= d; ++i) if (f[n][i] == mx) { ans[n] = s[i][k]; int nw = i; for (int j = n - 1; j >= k; --j) for (int l = 1; l <= d; ++l) if (flag[nw][l] and f[j + 1][nw] == f[j][l] * p[j + 1][s[nw][k] - 'a']) { ans[j] = s[l][k]; nw = l; break; } for (int j = k - 1; j; --j) ans[j] = s[nw][j]; break; } for (int i = 1; i <= n; ++i) cout << ans[i]; } void solve() { init(); calculate(); } int main() { ios::sync_with_stdio(false); cin.tie(nullptr), cout.tie(nullptr); int T = 1; while (T--) solve(); return 0; }
posted @   Steven1013  阅读(5)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
展开