• hdu 5009 Paint Pearls


    首先把具有相同颜色的点缩成一个点,即数据离散化。

    然后使用dp[i]表示涂满前i个点的最小代价。对于第i+1个点,有两种情况:

    1)自己单独涂,即dp[i+1] = dp[i] + 1

    2)从第k个节点之后(不包括k)到第i+1个节点一次涂完,且一起涂的节点共有num种颜色,即dp[i+1] = dp[k] + num * num

    从而可以得到状态转移方程dp[i+1] = min(dp[i], dp[k] + num * num)

    但是如果从后往前遍历每一个k,会超时。

    因此我们可以使用双向链表来把每种颜色最后出现的位置穿起来。对于每一个新加入的点,如果该点颜色没出现过,那么把它加入到双向链表的结尾。如果该点出现过,把它最后出现的位置从双向链表中删除,并把最新的位置加入到双向链表结尾。

    需要注意的是要建立一个头节点,使得第一个节点不会因为后面出现了同样的颜色而被删除,从而无法计算从头到尾一次性涂完的情况。

    第二个要注意的是如果num * num 已经大于了每个节点单独涂的代价 i,那么就没有必要再往前查找了。

    代码如下:

     1 #define MAXN 50005
     2 #include <stdlib.h>
     3 #include <iostream>
     4 #include <stdio.h>
     5 #include <map>
     6 #include <limits.h>
     7 
     8 using namespace std;
     9 int arr[MAXN];//输入
    10 int l[MAXN];//记录前一个最后出现的颜色的位置
    11 int r[MAXN];//记录后一个最后出现颜色的位置
    12 int dp[MAXN];//dp[i]表示涂到第i个节点最小的代价
    13 int m;//离散化后数组的长度
    14 
    15 void solve()
    16 {
    17     map<int, int> exist;//map 存储出当前现过得颜色中,最后出现的位置
    18     int last = 1;//双向链表尾
    19     l[0] = -1;//头节点
    20     r[0] = 1;//头节点
    21     l[1] = 0;
    22     exist[arr[1]] = 1;
    23     dp[0] = 0;
    24     dp[1] = 1;
    25 
    26     for( int i = 2 ; i < m ; i++ )
    27     {
    28        if( exist.count(arr[i]) == 0 )
    29        {
    30            r[last] = i;//添加到尾部
    31            l[i] = last;
    32            last = i;
    33            exist[arr[i]] = i;
    34        }
    35        else
    36        {
    37             int tmp = exist[arr[i]];
    38             r[l[tmp]] = r[tmp];//删除tmp
    39             l[r[tmp]] = l[tmp];//删除tmp
    40             r[last] = i;
    41             l[i] = last;
    42             last = i;
    43             exist[arr[i]] = i;
    44        }
    45 
    46        int k = last;
    47        dp[i] = dp[i-1]+1;
    48        int num = 1;
    49        while( l[k] >= 0 )
    50        {
    51            dp[i] = min(dp[i], dp[l[k]] + num*num);
    52            num++;
    53            k = l[k];
    54            if( num * num > i )//剪枝
    55            {
    56                break;
    57            }
    58        }
    59     }
    60     printf("%d
    ", dp[m-1]);
    61 }
    62 
    63 int main(int argc, char *argv[])
    64 {
    65     int n;
    66     while(scanf("%d", &n) != EOF)
    67     {
    68         int a;
    69         m = 1;
    70         for( int i = 1 ; i <= n ; i++ )//从1开始,位置0是头节点
    71         {
    72             scanf("%d", &a);
    73             if( i == 1 )
    74             {
    75                 arr[m++] = a;
    76             }
    77             else if( arr[m-1] !=  a )//合并相同颜色的节点,离散化
    78             {
    79                 arr[m++] = a;
    80             }
    81         }
    82         solve();
    83     }
    84     return 0;
    85 }
  • 相关阅读:
    rocketmq的linux搭建环境
    linux调用本地shell脚本
    第二课
    第一课
    有意思的dos攻击
    信息收集
    url跳转挖掘
    Drozer之android_app分析实战
    缓存中毒
    随便写写
  • 原文地址:https://www.cnblogs.com/jostree/p/3995813.html
Copyright © 2020-2023  润新知