1. 程式人生 > >【Foreign】動態規劃 [分治][DP]

【Foreign】動態規劃 [分治][DP]

tex oid ans script 決策 cst pen 因此 out

動態規劃  

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, i)。我們打一個表,發現決策具有單調性

  但是顯然,對於這道題,我們不能直接二分轉移來的位置,由於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

【Foreign】動態規劃 [分治][DP]