• 【Foreign】动态规划 [分治][DP]


    动态规划  

    Time Limit: 50 Sec  Memory Limit: 128 MB

    Description

      一开始有n个数,一段区间的价值为这段区间相同的数的对数。
      我们想把这n个数切成恰好k段区间。之后这n个数的价值为这k段区间的价值和。
      我们想让最终这n个数的价值和尽可能少。
      例如6个数1,1,2,2,3,3要切成3段,一个好方法是切成[1],[1,2],[2,3,3],这样只有第三个区间有1的价值。因此这6个数的价值为1。

    Input

      第一行两个数n,k。
      接下来一行n个数ai表示这n个数。

    Output

      一个数表示答案。   

    Sample Input

      10 2
      1 2 1 2 1 2 1 2 1 2

    Sample Output

      8

    HINT

      对于100%的数据1<=n<=100000,1<=k<=min(n,20),1<=ai<=n。

    Solution

      首先,暴力DP非常显然,f[i][j] 表示分了 i 段,当前做到第 j 个元素的最小值。

      那么 f[i][j] = f[i - 1][k] + sum(k + 1, j)。我们打一个表,发现决策具有单调性

      但是显然,对于这道题,我们不能直接二分转移来的位置,由于sum并不好求。

      所以我们可以考虑运用分治。执行k次。Solve(l, r, L, R)表示 j∈[l, r],from∈[L, R]

      那么我们对于[l, r],考虑mid[L, R]中的哪一个转移过来,假设是MidFrom

      那么由于决策单调性,所以[l, mid - 1]决策点一定在[L, MidFrom][mid + 1, r]决策点一定在[MidFrom, R]

      移动两个指针now_l, now_r维护sum即可。(复杂度我也不会证明呀QWQ)

    Code

     1 #include<iostream>
     2 #include<string>
     3 #include<algorithm>
     4 #include<cstdio>
     5 #include<cstring>
     6 #include<cstdlib>
     7 #include<cmath>
     8 using namespace std;
     9 typedef long long s64;
    10 
    11 const int ONE = 100005;
    12 const int MOD = 1e9 + 7;
    13 const s64 INF = 1e18;
    14 
    15 int get()
    16 {
    17         int res = 1, Q = 1; char c;
    18         while( (c = getchar()) < 48 || c > 57)
    19             if(c == '-') Q = -1;
    20         if(Q) res = c - 48;
    21         while( (c = getchar()) >= 48 && c <= 57)
    22             res = res * 10 + c - 48;
    23         return res * Q;
    24 }
    25 
    26 int n, k;
    27 int a[ONE], cnt[ONE];
    28 
    29 s64 record[ONE], f[ONE], value;
    30 int now_l, now_r;
    31 
    32 
    33 void Move(int l, int r)
    34 {
    35         while(now_r < r) cnt[a[++now_r]]++, value += cnt[a[now_r]];
    36         while(l < now_l) cnt[a[--now_l]]++, value += cnt[a[now_l]];
    37         while(now_r > r) value -= cnt[a[now_r]], cnt[a[now_r--]]--;
    38         while(l > now_l) value -= cnt[a[now_l]], cnt[a[now_l++]]--;
    39 }
    40 
    41 void Solve(int l, int r, int L, int R) //j=l~r, from = L~R
    42 {
    43         if(l > r) return;
    44         int mid = l + r >> 1, MidFrom;
    45         s64 Ans = INF;
    46         for(int from = L; from <= R; from++)
    47         {
    48             if(from >= mid) break;
    49             Move(from + 1, mid);
    50             if(f[from] + value < Ans)
    51                 Ans = f[from] + value, MidFrom = from;
    52         }
    53         record[mid] = Ans;
    54         Solve(l, mid - 1, L, MidFrom);
    55         Solve(mid + 1, r, MidFrom, R);
    56 } 
    57 
    58 int main()
    59 {
    60         n = get();    k = get();
    61         for(int i = 1; i <= n; i++)
    62             a[i] = get();
    63             
    64         for(int i = 0; i <= n; i++) f[i] = INF;
    65         f[0] = 0;
    66         for(int j = 1; j <= k; j++)
    67         {
    68             for(int i = 1; i <= n; i++) cnt[i] = -1;
    69             now_l = now_r = 1; value = 0, cnt[a[1]] = 0;
    70             Solve(1, n, 0, n - 1);
    71             for(int i = 1; i <= n; i++)
    72                 f[i] = record[i], record[i] = 0;
    73         }
    74         printf("%lld", f[n]);
    75 }
    View Code
  • 相关阅读:
    Ogre的骨骼动画
    ID卡读取方法(用于区分ID卡读取出来的数据和一般人手录入的数据)
    FastSpring学习笔记一
    数学 方程的解
    单调栈+桶+分治 奇袭
    神奇DP [HNOI2004] 打砖块
    DFS 找硬币
    树DP 树上染色
    android 适配器Adpter的使用总结 之 BaseExpandableListAdapter
    Java删除文件夹以及文件夹下的子目录与文件
  • 原文地址:https://www.cnblogs.com/BearChild/p/7800889.html
Copyright © 2020-2023  润新知