题目:
http://acm.hdu.edu.cn/showproblem.php?pid=5009
题意:
有一串珠子需要染色,允许每次染一段区间,代价为这段区间的颜色总数color^2,求最小代价
思路:
感觉就是DP,然而就是搞不出怎么DP给跪了
先提出一个基础的DP思路 dp[i] = min{dp[j]+num[j+1,i]^2},dp[i]代表[1,i]这段区间的代价,0<=j<i,num为对应区间的颜色总数,显然,这么构建状态转移方程的情况下时间复杂是O(n^2),需要优化
以第二组测试数据为例,dp[9] = min{dp[8] + 1, dp[7] + 4, dp[6]+ 9, dp[0] + 9},嗯...嗯?!中间的dp[54321]哪去了!!事实上,中间的d[6],dp[5],dp[4]..是不需要考虑的,回头看状态转移方程,事实上这么转移是将[j+1,i]视为一段区间进行全体染色,则dp[6]+9,代表dp[6] + 对颜色{2,3,4}进行区间染色,则无论是[1,9][2,9][3,9]..[6,9]这些区间都是对颜色{2,3,4}进行区间染色,代价相同没有考虑的必要,直接跳到dp[0]的状态进行判断
因此,可以发现,每种颜色只要保留最后出现的位置即可,实现方式为数组模拟双向链表,具体看代码,反正也不长……
代码:
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <map>
using namespace std;
const int MAXINT = 0x7FFFFFFF;
const int MAXSIZE = 5*1e4 + 100;
int dp[MAXSIZE];
int pre[MAXSIZE];
int nxt[MAXSIZE];
int arr[MAXSIZE];
map<int, int> M;
int main(){
int n;
while (scanf("%d",&n)!=EOF){
for (int i=1;i<=n;++i){
scanf("%d",arr+i);
pre[i] = i-1;
nxt[i] = i+1;
}
pre[0] = -1;
for (int i=1;i<=n;++i) dp[i] = MAXINT;
dp[0] = 0;
M.clear();
for (int i=1;i<=n;++i){
if (!M.count(arr[i])) M[arr[i]] = i;
else {
int t = M[arr[i]];
nxt[pre[t]] = nxt[t];
pre[nxt[t]] = pre[t];
M[arr[i]] = i;
}
int cnt = 1;
for (int j=pre[i];j!=-1;j=pre[j]){
dp[i] = min(dp[j]+cnt*cnt, dp[i]);
cnt++;
if (cnt*cnt>n) break;
}
}
printf("%d\n",dp[n]);
}
return 0;
}