zoukankan      html  css  js  c++  java
  • 【算法30】从数组中选择k组长度为m的子数组,要求其和最小

    原题链接:codeforce 267 Div2 C

    问题描述:

    给定长度为n的数组a[],从中选择k个长度为m的子数组,要求和最大。

    形式描述为:选择$k$个子数组[$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的子数组,然后选择其中和最小的。第一个长度为m的子数组开始位置可能为0...(k-1)*m,然后第二个子数组的下标?第三个子数组下标?太复杂了而且时间复杂度肯定超高,不能忍,换个方法吧。

    【思路2】再看一下问题,要求和最大,求最值问题十有八九都是DP问题,试试吧。DP题目子问题怎么定义是关键,然后这东西基本只能靠经验了(嗯,算法导论上就是这么说的)。从后往前考虑,那么对于最后一个元素,只有两种情况,被选中到子数组中或者没有被选到子数组中。如果被选中,那么首先计算最后m个元素的和,剩下的问题就化为从前面长度为n-m的数组中选择k-1组和最大的子数组。如果没选中最后一个,也好办,直接转化为从前面n-1个元素中选择k组和最大的子数组。分析后我们有:

    子问题定义:   dp[i][j] = 从前i个元素中选择j个子数组的最大和

    状态转移方程:  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,出现了overflow,想了半天也没想明白到底错在哪里了,脑子真是瓦特啦,记下来以免重蹈覆辙。

  • 相关阅读:
    Android OpenGL ES和OpenGL一起学(二)------理解Viewport(视口)和坐标系Android OpenGL ES篇(转帖)
    CSOM中如何取到managed metadata类型字段的类型信息
    "Value does not fall within the expected range" with managed metadata fields
    GLFW_KEY_KP_ADD和GLFW_KEY_KP_SUBTRACT
    OPENGL: WHY IS YOUR CODE PRODUCING A BLACK WINDOW?
    (转)真正的中国天气api接口xml,json(求加精) ...
    Mongo如何在多个字段中查询某个关键字?
    VS2010整合NUnit进行单元测试
    ASP.NET MVC3 学习心得------路由机制
    MVC3中 ViewBag、ViewData和TempData的使用和区别
  • 原文地址:https://www.cnblogs.com/python27/p/3984061.html
Copyright © 2011-2022 走看看