传送门:HDU 5009
题意:给出n个连续的珠子,珠子有不同的颜色,现将其划分成任意多段,每一段的代价为该段中颜色种数的平方,问划分的最小总代价和是多少。
总思路:dp[i]代表处理到第i个珠子的最小花费,显然dp[i] = min(dp[j] + num[i][j] * num[i][j])(0 < j <= i) num[i][j]代表[j...i]区间内的颜色种数。n^2转移显然不能接受。
优化方法1:用双向链表维护珠子的颜色,使得任意时刻已经遍历过的珠子中每种颜色都只保存一个最新位置,每次遇到一个重复的颜色,就将该颜色前一个位置从链表中删除,然后利用双向链表维护dp数组,因为链表每个节点都代表该位置有一个新的颜色,因此可以避免很多无用操作,但这还不够,如果给出序列为1,2,3。。。。n,那么链表就毫无用处了,因此我们还要加一个下界优化:显然dp[n] <= n,因为最差情况每个珠子分一段,这时dp[n] == n。
代码:
#include<bits/stdc++.h>
#define ll long long
#define pi acos(-1)
#define MAXN 50010
#define inf 0x3f3f3f3f
using namespace std;
typedef pair<int,int>P;
int a[MAXN], val[MAXN];
int pre[MAXN], nxt[MAXN];
int last[MAXN], dp[MAXN];
int main()
{
int n, m, id, cnt;
while(cin >> n)
{
for(int i = 1; i <= n; i++)
{
scanf("%d", a + i);
val[i] = a[i];
pre[i] = i - 1;
nxt[i] = i + 1;
last[i] = 0;
dp[i] = inf;
}
dp[0] = 0; pre[0] = -1;
sort(val + 1, val + n + 1);
m = unique(val + 1, val + n + 1) - val;
for(int i = 1; i <= n; i++)
{
a[i] = lower_bound(val + 1, val + m, a[i]) - val;
id = last[a[i]];
if(id){
pre[nxt[id]] = pre[id];
nxt[pre[id]] = nxt[id];
}
last[a[i]] = i;
cnt = 0;
for(int j = pre[i]; ~j; j = pre[j])
{
cnt++;
dp[i] = min(dp[i], dp[j] + cnt * cnt);
if(cnt * cnt >= n) break;
}
}
printf("%d\n", dp[n]);
}
return 0;
}
优化方法2:将连续的同样颜色的珠子缩成一个珠子(易证这样做不影响dp结果的正确性),然后再用下界进行剪枝就行了。代码详见:点击打开链接