【算法30】从数组中选择k组长度为m的子数组,要求其和最小
原題鏈接:codeforce 267 Div2 C
問題描述:
給定長度為n的數(shù)組a[],從中選擇k個長度為m的子數(shù)組,要求和最大。
形式描述為:選擇$k$個子數(shù)組[$l_1$,?$r_1$],?[$l_2$,?$r_2$],?...,?[$l_k$l1,?$r_k$]?(1?≤?$l_1$?≤$r_1$?≤$l_2$?≤?$r_2$?≤...?≤$l_k$?≤?$r_k$?≤?n; $r_i-r_i+1$),?使得$\sum_{i=1}^{k}\sum_{j=l_i}^{r_i}p_j$
問題分析:
【思路1】先從簡單粗暴的方法入手,怎么辦?尋找所有的k個長度為m的子數(shù)組,然后選擇其中和最小的。第一個長度為m的子數(shù)組開始位置可能為0...(k-1)*m,然后第二個子數(shù)組的下標(biāo)?第三個子數(shù)組下標(biāo)?太復(fù)雜了而且時間復(fù)雜度肯定超高,不能忍,換個方法吧。
【思路2】再看一下問題,要求和最大,求最值問題十有八九都是DP問題,試試吧。DP題目子問題怎么定義是關(guān)鍵,然后這東西基本只能靠經(jīng)驗了(嗯,算法導(dǎo)論上就是這么說的)。從后往前考慮,那么對于最后一個元素,只有兩種情況,被選中到子數(shù)組中或者沒有被選到子數(shù)組中。如果被選中,那么首先計算最后m個元素的和,剩下的問題就化為從前面長度為n-m的數(shù)組中選擇k-1組和最大的子數(shù)組。如果沒選中最后一個,也好辦,直接轉(zhuǎn)化為從前面n-1個元素中選擇k組和最大的子數(shù)組。分析后我們有:
子問題定義: dp[i][j] = 從前i個元素中選擇j個子數(shù)組的最大和
狀態(tài)轉(zhuǎn)移方程: dp[i][j] = max(dp[i-1][j], dp[i-m][j-1] + sum(a[i-m]...a[i-1]))
初始條件: ? ? ? dp[0][j] = 0; dp[i][0] = 0; if (i < j * m) dp[i][j] = 0;
AC代碼如下:
1 #include <iostream> 2 #include <string> 3 #include <vector> 4 #include <algorithm> 5 #include <functional> 6 #include <numeric> 7 using namespace std; 8 9 int main() 10 { 11 int n, m, k; 12 cin >> n >> m >> k; 13 14 vector<int> v(n, 0); 15 for (int i = 0; i < n; ++i) 16 { 17 cin >> v[i]; 18 } 19 20 // dp[i][j] = choose j pairs integers from the first i elements 21 // Then base on the ith is chosen or not, there are two case: 22 // not choose ith element, the dp[i][j] = dp[i-1][j] 23 // choose ith element, the dp[i][j] = dp[i-m][j-1] + sum(a[i-1]...a[i-m]) 24 // so dp[i][j] = max(dp[i-1][j], dp[i-m][j-1] + sum(a[i-1]...a[i-m]) 25 // base case: assert (i >= j * m) if not 0 dp[i][j] = 0 26 // the problem is equal to find dp[n][k] 27 28 vector<vector<long long> > dp(n+1, vector<long long>(k+1, 0)); 29 30 // base case 31 for (int i = 0; i < n + 1; ++i) 32 { 33 for (int j = 0; j < k + 1; ++j) 34 { 35 if (i < j * m) 36 { 37 dp[i][j] = 0; 38 } 39 } 40 } 41 42 // bottom to up 43 for (int i = 1; i < n + 1; ++i) 44 { 45 for (int j = 1; j < k + 1; ++j) 46 { 47 if (i >= j * m) 48 { 49 long long lastPairSum = accumulate(v.begin() + i - m, v.begin() + i, 0LL); 50 dp[i][j] = max(dp[i-1][j], dp[i-m][j-1] + lastPairSum); 51 } 52 53 } 54 } 55 56 long long ans = dp[n][k]; 57 cout << ans << endl; 58 return 0; 59 }注意點:
這道題目很簡單的,為什么要記錄下來呢,因為我用了int,出現(xiàn)了overflow,想了半天也沒想明白到底錯在哪里了,腦子真是瓦特啦,記下來以免重蹈覆轍。
轉(zhuǎn)載于:https://www.cnblogs.com/python27/p/3984061.html
總結(jié)
以上是生活随笔為你收集整理的【算法30】从数组中选择k组长度为m的子数组,要求其和最小的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Debian系列软件管理(第二版)
- 下一篇: linux面试题2