loj 3217 「PA 2019」Desant - 动态规划 - 复杂度分析
题目传送门
一个非常显然的想法是记录后面的值相邻两个之间在前面选了多少个数。
众所周知(比如我就不知道,我甚至以为它非常大),若干个和为 $n$ 的数的乘积最大为 $O(3^{n/3})$,最优方案是拆成若干个 3 和常数个 2。
然后 dp 即可。
时间复杂度 $O(n^23^{\frac{n + 1}{3}})$.
Code
#include <bits/stdc++.h> using namespace std; typedef bool boolean; const int N = 41; const int inf = (signed) (~0u >> 2); #define pii pair<int, long long> pii operator + (pii a, pii b) { if (a.first == b.first) return pii(a.first, a.second + b.second); return min(a, b); } int n; int p[N]; int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d", p + i); --p[i]; } vector<boolean> have (n, true); vector<int> lenc (n + 1, 1), lenp; vector<int> prodc (n + 2, 1), prodp; vector<pii> f {pii(0, 1)}, g; vector<int> vs; for (int i = 1; i <= n; i++) { swap(lenp, lenc); swap(prodp, prodc); swap(f, g); int id = 0; for (int j = 0; j < p[i]; j++) id += have[j]; have[p[i]] = false; lenc.clear(); prodc = {1}; int ls = -1; for (int j = 0; j < n; j++) { if (have[j]) { lenc.push_back(j - ls); prodc.push_back(prodc.back() * lenc.back()); ls = j; } } lenc.push_back(n - ls); prodc.push_back(prodc.back() * lenc.back()); f = vector<pii>(prodc.back(), pii(inf, 0)); for (int s = 0; s < prodp.back(); s++) { if (g[s].first == inf) continue; vs.clear(); int t = s; for (auto l : lenp) vs.push_back(t % l), t /= l; int dlt = 0; for (int j = id + 1; j < (signed) vs.size(); j++) dlt += vs[j]; vs[id] += vs[id + 1]; // cerr << id << " " << vs.size() << '\n'; vs.erase(vs.begin() + id + 1); int ns = 0; for (int j = 0; j < (signed) lenc.size(); j++) ns += vs[j] * prodc[j]; f[ns] = f[ns] + g[s]; g[s].first += dlt; ns += prodc[id]; f[ns] = f[ns] + g[s]; } } vector<pii> ans (n + 1, pii(inf, 0)); for (int s = 0; s < prodc.back(); s++) { if (f[s].first == inf) continue; vs.clear(); int t = s, len = 0; for (auto l : lenc) len += t % l, t /= l; ans[len] = ans[len] + f[s]; } for (int i = 1; i <= n; i++) { printf("%d %lld\n", ans[i].first, ans[i].second); } return 0; }