继上个月的分割数列(一)又出了这道题。
首先还是考虑 $n^2DP$ ,设 $f[i]$ 为分到 $i$ 个的最小权重之和。
转移枚举上一个在哪里分就行了。
显然时间会超限,我们考虑一下优化:
首先,如果有连续的数字相同,那么就把他们放到一个块里。
处理完后还是 $n^2DP$,这样时间复杂度就减少到了 $O($块数$^2)$。
接着我们再来考虑剪枝,显然把前面每一个块划分成一个数列是比较优秀的。
他的代价为前面块的数量,所以我们转移的时候,当前的 $f[i]$ 已经超过了块数
那就没必要继续下去了,直接 $break$ 掉,平均时间复杂度 $O(n\sqrt{n})$。
不过比如一些毒瘤数据:两个数交替出现。就会卡成 $O(n^2)$,所以可以再加一个 $set$ 优化。
那样时间复杂度就成了铁 $O(n\sqrt{n}\times log_n)$ 就很难过,所以我就这样吧。
$T3$ 还是状压改天在写。
贴代码:
#include <iostream> #include <cstring> #define int long long using namespace std; int n, cnt, cnt1, tot; int a[50005], f[50005], v[50005], tmp[50005], A[50005]; signed main() { scanf ("%d", &n); for (int i = 1; i <= n; i ++) { scanf ("%d", &a[i]); if (a[i] != a[i - 1]) A[++ tot] = a[i]; } f[0] = 0; for (int i = 1; i <= tot; i ++) { tmp[A[i] ] ++; if (tmp[A[i] ] == 1) cnt1 ++; f[i] = i; memset (v, 0, sizeof v); v[A[i]] = 1; int cnt = 1; for (int j = i; j >= 1; j --) { if (cnt * cnt > i) break; if (!v[A[j] ]) { cnt ++; v[A[j]] = 1; } f[i] = min (f[i], f[j - 1] + cnt * cnt); } } printf ("%d", f[tot]); }
标签:YACS,cnt,50005,int,题解,复杂度,甲组,数列 From: https://www.cnblogs.com/Xy-top/p/17113113.html