5-08 685 views
题目大意:给n个珠子涂成目标颜色。每次可以涂相邻的一些珠子,花费是这些珠子中颜色种数k的平方。求最小花费。
思路:双向链表优化dp。dp[i]表示涂完前i个所花的最小代价,显然有dp[i]=min{dp[j]+num(j+1,i)^2},其中1<=j<i,num(j+1,i)表示区间[j+1,i]的颜色个数。
这样复杂度为O(n^2)显然超时。那么需要优化一下,比如第二组测试数据3 4 2 4 4 2 4 3 2 2,假设dp[1]…dp[8]已更新完毕,现在要更新dp[9],可以看到a[9]为2,按照原始的dp[i]=min{dp[j]+num(j+1,i)^2},
i=9,枚举j=8,dp[9]=min{dp[9],dp[8]+1^2};j=7,dp[9]=min{dp[9],dp[7]+2^2};现在貌似没什么变化。。。
j=6,这里就神奇了,如果dp[9]=min{dp[9],dp[6]+3^2},那么这个就太弱了,因为此时2 4 3是连着涂的,但是2之前是3 4 2 4 4,这些如果跟着一起涂,那么仍然是3^2的代价,但前面的数字变少了,显然这种更优。
于是乎dp[9]=min{dp[9],dp[0]+3^2},可以看到6直接跳到了0,为什么这么跳?因为这之前都是些234啊,重复了没必要保存。所以在dp时,我们只需要维护好前后关系即可。
比如当前dp第i位,那么即a[i]加进来,所以之前如果有a[i]值的必须删掉,具体双向链表维护。因此可以看到任意时刻,每种颜色只会保存一次,复杂度就降下来了。
但仍然可以给出坑爹的数据,比如1 2 3 4 … n那么这个dp的话,复杂度仍为O(n^2),于是继续优化。我们知道如果一个一个涂,那么需要花费n。所以最优方案不能大于n,也就是不能连着sqrt(n)个不同的颜色一起涂,否则代价大于n了,这里进行剪枝。复杂度降为O(nsqrt(n)),还是可以接受的。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <map> using namespace std; const int maxn = 50010; const int inf = 0x3f3f3f3f; int a[maxn], pre[maxn], nxt[maxn], dp[maxn]; map<int, int> mp; int main() { int n; while (scanf("%d", &n) != EOF) { for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); pre[i] = i - 1; nxt[i] = i + 1; } mp.clear(); memset(dp, 0x3f, sizeof(dp)); dp[0] = 0, pre[0] = -1; for (int i = 1; i <= n; i++) { if (!mp.count(a[i])) mp[a[i]] = i; else { int id = mp[a[i]]; nxt[pre[id]] = nxt[id]; pre[nxt[id]] = pre[id]; mp[a[i]] = i; } int cnt = 0; for (int j = pre[i]; j != -1; j = pre[j]) { cnt++; dp[i] = min(dp[i], dp[j] + cnt * cnt); if (cnt * cnt > i) break; } } printf("%d\n", dp[n]); } return 0; }
- Ceph客户端缓存一致性分析 - 2019年6月21日
- CephFS:什么是CAPS? - 2019年6月21日
- Ceph集群快速搭建教程 - 2019年6月20日