3 1 3 3 10 3 4 2 4 4 2 4 3 2 2
2 7
这样复杂度为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<cstdio> #include<iostream> #include<cstring> #include<map> #define Maxn 50010 using namespace std; int a[Maxn],pre[Maxn],nxt[Maxn],dp[Maxn]; map<int,int> mp; int main() { int n; while(~scanf("%d",&n)){ 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; }