zoukankan      html  css  js  c++  java
  • BZOJ2933 [Poi1999]地图【区间DP】

    Description

    一个人口统计办公室要绘制一张地图。由于技术的原因只能使用少量的颜色。两个有相同或相近人口的区域在地图应用相同的颜色。例如一种颜色k,则A(k) 是相应的数,则有:
    在用颜色k的区域中至少有一半的区域的人口不大于A(k)
    在用颜色k的区域中至少有一半的区域的人口不小于A(k)
    区域颜色误差是该区域的人口与A(k)差的绝对值。累计误差是所有区域颜色误差的总和。我们要求出一种最佳的染色方案(累计误差最小)。
    任务
    写一个程序:
    读入每个区域的人口数
    计算最小的累计误差
    将结果输出

    Input

    第一行有一个整数n,表示区域数,10< n <3000。在第二行中的数m表示颜色数,2 <= m <= 10。在接下来的n中每行有一个非负整数,表示一个区域的人口。人口都不超过2^30。

    Output

    输出一个整数,表示最小的累计误差

    Sample Input

    11
    3
    21
    14
    6
    18
    10
    2
    15
    12
    3
    2
    2
    Sample Output
    15


    思路

    有贪心的思想,可以先排序,从小到大进行分块
    (dp_{i,j})表示前i个数分j个颜色
    然后(dp_{i,j}=min(dp_{k-1,j-1}+calc(k,i)))
    (calc(k,i)=sum_{p=k}^i |a[mid]-a[p]|)
    然后考虑怎么快速算calc
    发现每次在端点加上一个数,中位数会向右平移一位,然而平移前后原来和是不变的
    所以只需要统计当前加上这个数之后的贡献


    //Author: dream_maker
    #include<bits/stdc++.h>
    using namespace std;
    //----------------------------------------------
    //typename
    typedef long long ll;
    //convenient for
    #define fu(a, b, c) for (int a = b; a <= c; ++a)
    #define fd(a, b, c) for (int a = b; a >= c; --a)
    #define fv(a, b) for (int a = 0; a < (signed)b.size(); ++a)
    //inf of different typename
    const int INF_of_int = 1e9;
    const ll INF_of_ll = 1e18;
    //fast read and write
    template <typename T>
    void Read(T &x) {
      bool w = 1;x = 0;
      char c = getchar();
      while (!isdigit(c) && c != '-') c = getchar();
      if (c == '-') w = 0, c = getchar();
      while (isdigit(c)) {
        x = (x<<1) + (x<<3) + c -'0';
        c = getchar();
      }
      if (!w) x = -x;
    }
    template <typename T>
    void Write(T x) {
      if (x < 0) {
        putchar('-');
        x = -x; 
      }
      if (x > 9) Write(x / 10);
      putchar(x % 10 + '0');
    }
    //----------------------------------------------
    const int N = 3010;
    const int M = 20;
    int n, m, a[N];
    ll cal[N][N], dp[N][M];
    int main() {
      Read(n), Read(m);
      fu(i, 1, n) Read(a[i]);
      sort(a + 1, a + n + 1);
      fu(j, 2, n)
        fd(i, j - 1, 1)
          cal[i][j] = cal[i + 1][j] + a[(i + j + 1) >> 1] - a[i];
      fu(i, 0, n)
        fu(j, 0, m) dp[i][j] = INF_of_ll;
      dp[0][0] = 0;
      fu(i, 1, n)
        fu(j, 1, m)
          fu(k, 1, i) 
            dp[i][j] = min(dp[i][j], dp[k - 1][j - 1] + cal[k][i]);
      Write(dp[n][m]);
      return 0;
    }
    
  • 相关阅读:
    石子合并问题(直线版)
    Python_07-常用函数
    Python_06-函数与模块
    C++中的头文件和源文件
    sell 项目 商品表 设计 及 创建
    SpringBoot集成Mybatis
    SpringBoot集成jdbcTemplate/JPA
    SpringBoot使用JSP渲染页面
    SpringBoot引入freemaker前端模板
    使用SpringBoot创建Web项目
  • 原文地址:https://www.cnblogs.com/dream-maker-yk/p/9817408.html
Copyright © 2011-2022 走看看