• DPHARD


    此篇收集各类DP题。

    《1D1D动态规划优化初步》的3个模型

    1. f[x] = min(f[i]+w[i, x]), i < x且w[i, x]满足单调性(即w[i, j]+w[i+1, j+1] <= w[i, j+1]+w[i+1, j],下四边形同)

    2. f[x] = min(g[i])+w[x], b[x] <= i < x, b[x]单调递增

    3. f[i]=max{x[j]*a[i]+y[j]*b[i]} = b[i]*max{a[i]/b[i]*x[j]+y[j]},变成了斜率优化

    一. 四边形不等式

    eg. Hiho1621:超市规划

    dp[i, j] = min{ dp[k, j-1] + w[k+1, i] }, 把长度为i的序列切分成j段,可糙猛快分治

    dp[i, j] = min{ dp[i, k-1] + dp[k, j] + w[i, j] }, 合并[i, j]区间。注意决策点应该在哪些范围之内单调,比如对于区间dp的方程来说,决策点的单调范围就应该是行号小于等于列号的那一部分。

    以上形式通常情况下w会满足四边形不等式,会使得决策opt[i, j] 与 opt[i, j-1], opt[i-1, j]满足单调性。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int N = 2010;
     4 const double inf = 1e17;
     5 double dp[N][N], c[N][N];
     6 double a[N], sum1[N], sum2[N], opt[N][N];
     7 int main()
     8 {
     9     int n, m;
    10     scanf("%d%d",&n,&m);
    11     for(int i = 1; i <= n; i++) 
    12         scanf("%lf", a+i);
    13     sort(a+1, a+n+1);
    14     for(int i = 1; i <= n; i++) {
    15         sum1[i] = sum1[i-1]+a[i];
    16         sum2[i] = sum2[i-1]+a[i]*a[i];
    17     }
    18     for(int i = 1; i <= n; i++)
    19      for(int j = i+1; j <= n; j++){
    20          double mid = (sum1[j]-sum1[i-1])/(j-i+1);
    21          c[i][j] = (sum2[j]-sum2[i-1])+(j-i+1)*mid*mid-2*mid*(sum1[j]-sum1[i-1]);
    22      }
    23     for(int i = 1; i <= m; i++)
    24      for(int j = 1; j <= n; j++)
    25       dp[i][j] = inf;
    26     for(int i = 1; i <= n; i++) dp[1][i] = c[1][i];
    27     for(int i = 2; i <= m; i++){
    28         opt[i][n+1] = n;
    29         for(int j = n; j >= 1; j--){
    30             for(int k = opt[i-1][j]; k <= opt[i][j+1]; k++)
    31              if(dp[i][j] > dp[i-1][k]+c[k+1][j]){
    32                     opt[i][j] = k;
    33                     dp[i][j] = dp[i-1][k]+c[k+1][j];
    34              }
    35         }
    36     }
    37     printf("%.3lf
    ", dp[m][n]);
    38     return 0;
    39 }
    View Code

    决策单调且不要求在线的糙快猛优化方法(for内写成i <= dr && i < m会有bug,m = 1的时候,循环无法进入,导致f[1]值无穷大)

     1 //个人感觉适用于状态数为二维的dp, 下一行(列)由前一行(列)推出, 且满足决策单调性
     2 void solve(int l, int r, int dl, int dr) {
     3     if(l > r) return ;
     4     int m = (l+r) >> 1, dm;
     5     for(int i = dl; i <= dr; i++) {
     6         if(i更新f[m]更优) {
     7            dm = i;
     8             更新f[m];
     9         }
    10     }
    11     solve(l, m-1, dl, dm), solve(m+1, r, dm, dr);
    12 }

     基于决策单调的糙猛快分治

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int N = 2010;
     4 const double inf = 1e17;
     5 double dp[N][N], c[N][N];
     6 double a[N], sum1[N], sum2[N], opt[N][N];
     7 void solve(double *f1, double *f2, int l, int r, int dl, int dr) {
     8     if(l > r) return ;
     9     int m = (l+r) >> 1, dm;
    10     for(int i = dl; i <= dr; i++) {
    11         if(f1[i]+c[i+1][m] < f2[m])
    12             f2[m] = f1[i]+c[i+1][m], dm = i;
    13     }
    14     solve(f1, f2, l, m-1, dl, dm);
    15     solve(f1, f2, m+1, r, dm, dr);
    16 }
    17 int main() {
    18     int n, m;
    19     scanf("%d%d",&n,&m);
    20     for(int i = 1; i <= n; i++) 
    21         scanf("%lf", a+i);
    22     sort(a+1, a+n+1);
    23     for(int i = 1; i <= n; i++) {
    24         sum1[i] = sum1[i-1]+a[i];
    25         sum2[i] = sum2[i-1]+a[i]*a[i];
    26     }
    27     for(int i = 1; i <= n; i++)
    28      for(int j = i+1; j <= n; j++){
    29          double mid = (sum1[j]-sum1[i-1])/(j-i+1);
    30          c[i][j] = (sum2[j]-sum2[i-1])+(j-i+1)*mid*mid-2*mid*(sum1[j]-sum1[i-1]);
    31      }
    32 
    33     for(int i = 0; i <= m; i++)
    34      for(int j = 1; j <= n; j++)
    35       dp[i][j] = inf;
    36     for(int i = 1; i <= n; i++) dp[1][i] = c[1][i];
    37     for(int i = 2; i <= m; i++) 
    38         solve(dp[i-1], dp[i], 1, n, 1, n);
    39     printf("%.3f
    ", dp[m][n]);
    40     return 0;
    41 }
    View Code

    eg. hiho1933 

    四边形不等式优化

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 const int N = 1e4+5;
     4 
     5 long long a[N], sum[N], dp[N][1005];
     6 int opt[N][1005];
     7 
     8 long long get_sum(int l, int r) {
     9     int m = (l+r) >> 1;
    10     long long ans1 = a[m]*(m-l)-(sum[m-1]-sum[l-1]);
    11     long long ans2 = (sum[r]-sum[m])-a[m]*(r-m);
    12     return ans1+ans2;
    13 }
    14 
    15 int main() {
    16     int n, k; cin >> n >> k;
    17     for(int i = 1, x, y; i <= n; i++) {
    18         cin >> x >> y;
    19         a[i] = y;
    20     }
    21     sort(a+1, a+n+1);
    22     for(int i = 1; i <= n; i++) sum[i] = sum[i-1]+a[i];
    23 
    24     memset(dp, 0x3f, sizeof(dp)); 
    25     for(int i = 0; i <= k; i++) dp[0][i] = 0; //这里i <= k写成了i <= n, 一直WA
    26     for(int i = 1; i <= n; i++) {
    27         opt[i][k+1] = n;
    28         for(int j = k; j; j--) {
    29             for(int kk = opt[i-1][j]; kk <= opt[i][j+1]; kk++) {
    30                 long long tmp =  dp[kk][j-1]+get_sum(kk+1, i);
    31                 if(tmp < dp[i][j]) 
    32                     dp[i][j] = tmp, opt[i][j] = kk;
    33             }
    34         }
    35     }
    36     cout << dp[n][k] << endl;
    37     return 0;
    38 }
    View Code

     基于决策单调的糙猛快分治

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 const int N = 1e4+5;
     4 
     5 long long a[N], sum[N], dp[2][N];
     6 
     7 long long get_sum(int l, int r) {
     8     if(l >= r) return 0;
     9     int m = (l+r) >> 1;
    10     long long ans1 = a[m]*(m-l)-(sum[m-1]-sum[l-1]);
    11     long long ans2 = (sum[r]-sum[m])-a[m]*(r-m);
    12     return ans1+ans2;
    13 }
    14 
    15 void solve(long long *f1, long long *f2, int l, int r, int dl, int dr) {
    16     if(l > r) return ;
    17     int m = (l+r) >> 1, dm;
    18     for(int i = dl; i <= dr; i++) {
    19         long long tmp = f1[i]+get_sum(i+1, m);
    20         if(tmp < f2[m]) {
    21             f2[m] = tmp;
    22             dm = i;
    23         }
    24     }
    25     solve(f1, f2, l, m-1, dl, dm);
    26     solve(f1, f2, m+1, r, dm, dr);
    27 }
    28 
    29 int main() {
    30     int n, k; cin >> n >> k;
    31     for(int i = 1, x, y; i <= n; i++) {
    32         cin >> x >> y;
    33         a[i] = y;
    34     }
    35     sort(a+1, a+n+1);
    36     for(int i = 1; i <= n; i++) sum[i] = sum[i-1]+a[i];
    37 
    38     for(int i = 0; i <= n; i++)
    39         dp[1][i] = get_sum(1, i);
    40     for(int i = 2; i <= k; i++) {
    41         bool now = (i&1);
    42         memset(dp[now], 0x3f, sizeof(dp[now]));
    43         solve(dp[!now], dp[now], 1, n, 1, n);
    44     }
    45     cout << dp[k&1][n] << endl;
    46     return 0;
    47 }
    View Code

    二. 斜率优化

    eg. HDU3507

    f[i] = min(f[j]+(s[i]-s[j])²)+M

         = min(f[j]+s[j]²-2s[i]s[j])+M+s[i]²

         = min(kx+b)+M+s[i]², 其中k = -2s[j], b = f[j]+s[j]², x = s[i]

    注:你也可看作k = 2s[i], x = s[j], y = f[j]+s[j]², f[i] = min(y-kx), k是定值,

           则求k为定值的直线y = kx+b的最小截距。脑补画面,将斜率为k的直线移到最下端....

           个人认为将整个min里的值当作y,当前已知值作为自变量比较好...

    其它题目的变形:f[i]=max{x[j]*a[i]+y[j]*b[i]} = b[i]*max{a[i]/b[i]*x[j]+y[j]}

    因为求min,所以维护倒U形(上凸)

    因为k递减,x递增,所以用单调队列维护即可,队列内直线按k递减排序,维护倒U形。

    代码区===待填===

    ps.

    如果x无序,则每次二分查找;

    如果k无序,可以用set维护,插入的时候erase掉两侧不符合的直线;

    如果x, k都无序,可以用set按k值排序维护直线,查找的时候再二分k,lowerbound计算在哪条斜率的直线上(判断x值是否在该直线的两端点内,不在则继续二分)

    三、WQS二分

    作用就是题目给了一个选物品的限制条件,要求刚好选m个,让你最大化(最小化)权值,然后其特点就是当选的物品越多的时候权值越大(越小)。

    http://codeforces.com/blog/entry/49691

    https://www.cnblogs.com/HocRiser/p/9834069.html

    https://www.cnblogs.com/flashhu/p/9480669.html

    https://www.cnblogs.com/CreeperLKF/p/9045491.html

    =================================================================================================

    区间dp

    括号dp:

    hihocoder1891:有多少种填充*的方案使得括号匹配?

      1 #include <bits/stdc++.h>
      2 
      3 #define ll long long
      4 #define ull unsigned long long
      5 #define st first
      6 #define nd second
      7 #define pii pair<int, int>
      8 #define pil pair<int, ll>
      9 #define pli pair<ll, int>
     10 #define pll pair<ll, ll>
     11 #define tiii tuple<int, int, int>
     12 #define pw(x) ((1LL)<<(x))
     13 #define lson l, m, rt<<1
     14 #define rson m+1, r, rt<<1|1
     15 #define sqr(x) ((x)*(x))
     16 #define SIZE(A) ((int)(A.size()))
     17 #define LENGTH(A) ((int)(A.length()))
     18 #define FIN freopen("A.in","r",stdin);
     19 #define FOUT freopen("A.out","w",stdout);
     20 using namespace std;
     21 /***********/
     22 
     23 template<typename T>
     24 bool scan (T &ret) {
     25     char c;
     26     int sgn;
     27     if (c = getchar(), c == EOF) return 0; //EOF
     28     while (c != '-' && (c < '0' || c > '9') ) c = getchar();
     29     sgn = (c == '-') ? -1 : 1;
     30     ret = (c == '-') ? 0 : (c - '0');
     31     while (c = getchar(), c >= '0' && c <= '9') ret = ret * 10 + (c - '0');
     32     ret *= sgn;
     33     return 1;
     34 }
     35 template<typename N,typename PN>inline N flo(N a,PN b){return a>=0?a/b:-((-a-1)/b)-1;}
     36 template<typename N,typename PN>inline N cei(N a,PN b){return a>0?(a-1)/b+1:-(-a/b);}
     37 template<typename T>inline int sgn(T a) {return a>0?1:(a<0?-1:0);}
     38 template<class T> int countbit(const T &n) { return (n==0)?0:(1+countbit(n&(n-1))); }
     39 template <class T1, class T2>
     40 bool gmax(T1 &a, const T2 &b) { return a < b? a = b, 1:0;}
     41 template <class T1, class T2>
     42 bool gmin(T1 &a, const T2 &b) { return a > b? a = b, 1:0;}
     43 template <class T> inline T lowbit(T x) {return x&(-x);}
     44 
     45 template<class T1, class T2>
     46 ostream& operator <<(ostream &out, pair<T1, T2> p) {
     47     return out << "(" << p.st << ", " << p.nd << ")";
     48 }
     49 template<class A, class B, class C>
     50 ostream& operator <<(ostream &out, tuple<A, B, C> t) {
     51     return out << "(" << get<0>(t) << ", " << get<1>(t) << ", " << get<2>(t) << ")";
     52 }
     53 template<class T>
     54 ostream& operator <<(ostream &out, vector<T> vec) {
     55     out << "("; for(auto &x: vec) out << x << ", "; return out << ")";
     56 }
     57 void testTle(int &a){
     58     while(1) a = a*(ll)a%1000000007;
     59 }
     60 const ll inf = 0x3f3f3f3f;
     61 const ll INF = 1e17;
     62 const int mod = 1000000007;;
     63 const double eps = 1e-5;
     64 const int N = 100000+10;
     65 const double pi = acos(-1.0);
     66 
     67 /***********/
     68 
     69 char s[N];
     70 long long dp[2][N];
     71 void add(long long &x, long long y) {
     72     x += y;
     73     if(x >= mod) x %= mod;
     74 }
     75 int main() {
     76     scanf("%s", s);
     77     int tag = 0, modify = 0, base = 0, l = 1;
     78     dp[0][0] = 1;
     79     for(int i = 0; s[i]; i++) {
     80         if(s[i] == '(') 
     81             base++;
     82         else if(s[i] == ')') {
     83             if(base) base--;
     84             else {
     85                 l--;
     86                 if(l == 0) { puts("0"); return 0; }
     87                 modify++;
     88             }
     89         }
     90         else {
     91             if(modify) {
     92                 tag = !tag;
     93                 for(int j = 0; j < l; j++)
     94                     dp[tag][j] = dp[!tag][j+modify];
     95                 modify = 0;
     96             }
     97             tag = !tag;
     98             if(base) {
     99                 memset(dp[tag], 0, sizeof(long long)*(l+2));
    100                 for(int j = 0; j < l; j++) {
    101                     add(dp[tag][j], dp[!tag][j]);
    102                     add(dp[tag][j+2], dp[!tag][j]);
    103                 }
    104                 base--, l += 2;
    105             }
    106             else {
    107                 memset(dp[tag], 0, sizeof(long long)*(l+1));
    108                 for(int j = 0; j < l; j++) {
    109                     if(j) add(dp[tag][j-1], dp[!tag][j]);
    110                     add(dp[tag][j+1], dp[!tag][j]);
    111                 }
    112                 l++;
    113             }
    114         }
    115     }
    116     if(modify) {
    117         tag = !tag;
    118         for(int j = 0; j < l; j++)
    119             dp[tag][j] = dp[!tag][j+modify];
    120         modify = 0;
    121     }
    122     printf("%lld
    ", base? 0: dp[tag][0]);
    123     return 0;
    124 }
    View Code
  • 相关阅读:
    【解题报告】洛谷P1038 神经网络
    【解题报告】洛谷P6475 建设城市
    【解题报告】洛谷P4138 挂饰
    【解题报告】洛谷P3870 开关
    【解题报告】洛谷P1120 小木棍
    洛谷P1168 中位数
    FWT(快速沃尔什变换)
    lucas和扩展lucas
    exBSGS
    2_sat
  • 原文地址:https://www.cnblogs.com/dirge/p/7789932.html
Copyright © 2020-2023  润新知