求解一道DP(也可能不是DP?)

学术版

我有个大胆的想法不知道对不对,长度为1的情况下没法换,长度为2的情况下最多换1一次,3最多换3次$(cba)$,4最多换6次,在长度小于等于26的情况下最多换$1+2+..+26$次,在长度大于26的情况下没多一个字母可以增添$26$次,根据这个应该就能写了吧。
by 星空舞涵 @ 2021-02-27 08:53:35


最多换$1+2+...+25$次,上面打错了,每次也是增添$25$
by 星空舞涵 @ 2021-02-27 08:54:38


@[星空舞涵](/user/243750) 这个可以算出答案对应的长度,但是如果要算出具体字符串的做法怎么弄呀
by CodyTheWolf @ 2021-02-27 11:48:31


@[星空舞涵](/user/243750) 26个以后应该不是增添25个,例如26 ~ 52 ,长度为 L 增加到 L + 1,应该是增加 L - 1 个 (不一样的字母有L - 1个,且都能成逆序对)
by CodyTheWolf @ 2021-02-27 11:54:52


写出来了,跟原题解不一样在于舍弃了f数组,直接计算chcnt数组(在我这就是dp数组) DP - $dp[i][j]$ 表示 长度为 i 的 且以 字母('a' + j - 1) 开头的字符串,最大逆序对数量 - 转移的话枚举 k < i 那么从上个字母转移到现在增加的逆序对就是 k * (i - k) 构造字符串 - 找出 i 最小,其次 j 最小的,并且dp[i][j] >= 0 的 i , j - 从长为 i ,以字母('a' + j - 1)开头,分三种情况讨论 - - 如果j == 1,就不能令字典序再小了,只能输出'a' - - 如果dp[i][j - 1] >= v 说明当前字符可以变小而且变小后,后面的字符串仍然可以凑足逆序对数,剩下要凑的逆序对为 v - (i - 1) - - 否则此时不能换字符,只能延续,剩下要凑的逆序对为 v - (i - 1) + same (v -= i - 1 - same) 这里的MAXN * (MAXN - 1) == v 所以复杂度大概为O(26v) ``` #include <iostream> #include <cstring> using namespace std; constexpr size_t MAXN = 5e3; int dp[MAXN][27]; int v; signed main() { ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); memset(dp, 0x80, sizeof dp), dp[0][0] = 0; cin >> v; for (int i = 1; i < MAXN; i++) for (int j = 1; j <= 26; j++) for (int k = 0; k < i; k++) dp[i][j] = max(dp[i][j], dp[k][j - 1] + k * (i - k)); int i = 0, j = 0, same = 0, flag = 1; while (flag) { j = 1, i++; for (; j <= 26; j++) { if (dp[i][j] >= v) { flag = 0; break; } } } while (i) { if (j == 1) cout << (char)'a'; else if (dp[i][j - 1] >= v) j--, cout << (char)(j + 'a' - 1), v -= i - 1, same = 1; else cout << (char)(j + 'a' - 1), v -= i - 1 - same, same++; i--; } } ```
by CodyTheWolf @ 2021-02-27 14:00:12


前排orz
by 血虎之爪 @ 2021-03-04 15:51:42


|