• CF868F Yet Another Minimization Problem 分治决策单调性优化DP


    题意:

      给定一个序列,你要将其分为k段,总的代价为每段的权值之和,求最小代价。

      定义一段序列的权值为$sum_{i = 1}^{n}{inom{cnt_{i}}{2}}$,其中$cnt_{i}$表示当前这段序列中数字大小为i的数的个数。

    题解:

      先考虑暴力DP, f[i][j]表示DP到i位,分为j段的最小代价。

      则$f[i][j] = min(f[l - 1][j] + sum[l][i])$,其中sum[l][i]表示区间[l, i]分成一段的代价。

      然后可以发现,这是具有决策单调性的,简易证明:

        首先设l < j < i.

        假设f[i][t]从f[j - 1][t - 1]转移而来,则有$f[j - 1][t - 1] + w[j][i] < f[l - 1][t - 1] + w[l][i]$.

        现在考虑i + 1的情况,观察到$inom{cnt_{i}}{2}$的式子经过化简后恰好可以表示0 ~ n - 1这个等差数列的求和公式,因此w[j][i]可以O(1)的转移到w[j][i + 1],即w[j][i + 1] = w[j][i] + sum[j][i];.sum[j][i]表示这个区间内某个颜色的数量(懒得再打一维了,这个理解一下就好,只是这么表示而已)

        所以f[i + 1][t] = min(f[j - 1][t - 1] + w[j][i] + sum[j][i], f[l - 1][t - 1] + w[l][i] + sum[l][i]);//可以发现,由于$l < j$,所以$sum[l][i] >= sum[j][i]$,而式子的另一部分,也就是和f[i][t]相同的部分也是左边小于右边,因此j一定比l优。

       因此可以利用决策单调性来优化DP,因为无法O(1)得知w[i][j]的值,因此无法用二分单调栈来进行优化,于是我们考虑分治。

       不知道如何用分治优化决策单调性戳:决策单调性优化DP

      然后注意到对于这题而言,暴力转移的复杂度还是太高了,于是考虑利用以下之前已有的信息,设当前已经被求出权值的区间为[ll, rr],权值为rnt.

      那么每次转移的时候暴力将这个区间转移至当前需要的区间,于是就可以做到重复利用之前已经求出的信息。   

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define R register int
     4 #define AC 101000
     5 #define LL long long
     6 #define inf 1e18
     7 
     8 int n, k, now, ll, rr;
     9 int s[AC], sum[AC];
    10 LL f[AC][22], rnt;
    11 
    12 inline int read()
    13 {
    14     int x = 0;char c = getchar();
    15     while(c > '9' || c < '0') c = getchar();
    16     while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    17     return x;
    18 }
    19 
    20 void pre()
    21 {
    22     n = read(), k = read();
    23     for(R i = 1; i <= n; i ++) s[i] = read();
    24 }
    25 
    26 void cal(int l, int r)
    27 {
    28     while(rr < r) rnt += sum[s[++ rr]] ++;
    29     while(rr > r) rnt -= -- sum[s[rr --]];
    30     while(ll > l) rnt += sum[s[-- ll]] ++;
    31     while(ll < l) rnt -= -- sum[s[ll ++]];    
    32 }
    33 
    34 void solve(int l, int r, int kl, int kr)//当前区间[l, r],决策点区间[kl, kr]
    35 {
    36     if(l > r) return ;
    37     int mid = (l + r) >> 1, k = -1, b = min(mid, kr);
    38     for(R i = kl; i <= b; i ++)//枚举当前段开头
    39     {
    40         cal(i, mid);
    41         if(rnt + f[i - 1][now - 1] < f[mid][now])
    42             f[mid][now] = rnt + f[i - 1][now - 1], k = i;
    43     }
    44     solve(l, mid - 1, kl, k), solve(mid + 1, r, k, kr);
    45 }
    46 
    47 void work()
    48 {
    49     for(R i = 1; i <= n; i ++)
    50         for(R j = 0; j <= k; j ++) f[i][j] = inf;
    51     ll = 1, rr = n;
    52     for(R i = 1; i <= n; i ++) rnt += sum[s[i]] ++;
    53     for(now = 1; now <= k; now ++) solve(1, n, 1, n);//分层做k次
    54     printf("%lld
    ", f[n][k]);
    55 }
    56 
    57 int main()
    58 {
    59     freopen("in.in", "r", stdin);
    60     pre();
    61     work();
    62     fclose(stdin);
    63     return 0;
    64 }
    View Code
  • 相关阅读:
    sqlserver内存释放
    Windows任务管理器中内存使用、虚拟内存区别及与页面文件的关系
    GetMessage
    String.Format(string, arg0)中sring格式
    C#基础--之数据类型
    C# Socket
    C# 对象 序列化 XML
    C# Monitoring-network
    Nginx 网址
    WinSCP 连接 Ubuntu 拒绝的问题
  • 原文地址:https://www.cnblogs.com/ww3113306/p/9886828.html
Copyright © 2020-2023  润新知