【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

 

posted @ 2017-11-07 20:22  BearChild  阅读(374)  评论(0编辑  收藏  举报