USACO 5.5.3Two Five
Posted albertxwz
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了USACO 5.5.3Two Five相关的知识,希望对你有一定的参考价值。
题意
一个5*5字符矩阵,每个字符比它左面和上面的字符都要大,它可以这样转换成一个字符串:下一行接在本行行末。所以是一个长度为25的字符串(包含A~Y的字符)。把符合条件的矩阵转换成字符串后,按字典序排序并依次编号。要求编号和字符串互相转换。
题解
刚开始就觉得以前做过很多类似这样的题了。但是它的限制条件相对来说比较多,也没有什么特殊规律。所以只好计数DP了。
先看编号转字符串,枚举字符串开头(即一行一行地填字符矩阵),比如说“AB”开头的字符串有多少个?如果大于要求的编号的话,那么自然答案字符串的开头就是“AB”了。这个思想十分容易理解。
字符串转编号,类似地!如果这个字符串是“AC~~~~~”,我们先加上"AB~~~~"的字符串个数,然后一直逼近,直至相同。
好了。那么怎么统计以“AB”或者其他开头的字符串个数呢?
搞个5维数组DP。每一维表示这一行填了多少个。那么F[5][5][5][5][5]=1了。此乃边界是也。
具体看下代码,比较好理解啦。
哦对对,算下时间复杂度先。O(55)*O(52)=O(57)=O(78125)
代码
/* TASK:twofive LANG:C++ */ #include <cstdio> #include <cstring> #include <algorithm> using namespace std; int f[6][6][6][6][6], code, len[5], t[5], mode, maxr[5], maxc[5]; char cmd[2], chmat[5][5]; bool v[25]; int dp(int a, int b, int c, int d, int e, int k) { int &ans = f[a][b][c][d][e]; if (ans) return ans; if (!v[k]) return dp(a, b, c, d, e, k + 1); if (a < 5 && k > maxr[0] && k > maxc[a]) ans += dp(a+1, b, c, d, e, k+1); if (b < a && k > maxr[1] && k > maxc[b]) ans += dp(a, b+1, c, d, e, k+1); if (c < b && k > maxr[2] && k > maxc[c]) ans += dp(a, b, c+1, d, e, k+1); if (d < c && k > maxr[3] && k > maxc[d]) ans += dp(a, b, c, d+1, e, k+1); if (e < d && k > maxr[4] && k > maxc[e]) ans += dp(a, b, c, d, e+1, k+1); return ans; } int main() { freopen("twofive.in", "r", stdin); freopen("twofive.out", "w", stdout); scanf("%s\n", cmd); memset(len, 0, sizeof(len)); code = 0; memset(chmat, 0, sizeof(chmat)); if (cmd[0] == ‘N‘) scanf("%d", &code), mode = 0; else { for (int i = 0; i < 5; ++i) for (int j = 0; j < 5; ++j) scanf("%c", &chmat[i][j]); mode = 1; } memset(v, true, sizeof(v)); memset(maxr, -1, sizeof(maxr)); memset(maxc, -1, sizeof(maxc)); for (int i = 0; i < 5; ++i) { len[i] = 0; for (int j = 0; j < 5; ++j) { len[i]++; if (mode == 0) { for (int k = 0; k < 25; ++k) if (v[k] && k > maxr[i] && k > maxc[j]) { memset(f, 0, sizeof(f)); f[5][5][5][5][5] = 1; v[k] = false; maxr[i] = maxc[j] = k; int tmp = dp(len[0], len[1], len[2], len[3], len[4], 0); if (code <= tmp) { chmat[i][j] = ‘A‘ + k; break; } else { v[k] = true; code -= tmp; } } } else { for (int k = 0; k < chmat[i][j] - ‘A‘; ++k) if (v[k] && k > maxr[i] && k > maxc[j]) { memset(f, 0, sizeof(f)); f[5][5][5][5][5] = 1; v[k] = false; maxr[i] = maxc[j] = k; code += dp(len[0], len[1], len[2], len[3], len[4], 0); v[k] = true; } v[chmat[i][j] - ‘A‘] = false; } } } if (mode == 0) { for (int i = 0; i < 5; ++i) for (int j = 0; j < 5; ++j) printf("%c", chmat[i][j]); printf("\n"); } else printf("%d\n", code + 1); return 0; }
以上是关于USACO 5.5.3Two Five的主要内容,如果未能解决你的问题,请参考以下文章
P2750 贰五语言Two Five USACO5.5 记忆化搜索