• 区间dp的典例


    区间dp, 属于dp的一种,顾名思义,便是对区间处理的dp,其中石子归并,括号匹配,整数划分最为典型。

    (1)石子归并

    dp三要素:阶段,状态,决策。

    首先我们从第i堆石子到第j堆石子合并所花费的最小费用设为dp[i][j], 然后去想状态转移方程,dp[i][j]必然有两堆石子合并而来, 那么我们很快就可以推出状态转移方程为dp[i][j] = min(dp[i][j], dp[i][k] + dp[k+1][j] + s);(s为两堆石子的总和)

    下面附上代码

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <algorithm>
     4 using namespace std;
     5 const int N = 200 + 5;
     6 int a[N], dp[N][N], n, sum[N];
     7 
     8 void work(){
     9     for(int l = 1; l <= n; l ++){
    10         for(int i = 1; i + l <= n; i ++){
    11             int j = i + l;
    12             for(int k = i; k <= j; k ++){
    13                 dp[i][j] = min(dp[i][j], dp[i][k] + dp[k+1][j] + sum[j] - sum[i-1]);
    14             }
    15         }
    16     }
    17     printf("%d\n", dp[1][n]);
    18 }
    19 
    20 int main(){
    21     while(scanf("%d", &n) == 1){
    22         memset(dp, 0x3f,sizeof(dp));
    23         for(int i = 1; i <= n; i ++){
    24             scanf("%d", a + i);
    25             sum[i] = sum[i-1] + a[i];
    26             dp[i][i] = 0;
    27         }
    28         work();
    29     }
    30     return 0;
    31 }
    View Code

    当然还有变形题

    思路差不多只不过把两个数的和改成积(ps:在处理前缀和的时候千万别取余,否则可能出现负数)

    附上代码:

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <algorithm>
     4 using namespace std;
     5 const int N = 200 + 5;
     6 int a[N], dp[N][N], n, ans, sum[N];
     7 
     8 void  work(){
     9     for(int l  = 2; l <= n; l ++){
    10         for(int i = 1; i <= n - l + 1; i ++){
    11             int j = i + l - 1;
    12             for(int k = i; k < j; k ++){
    13                 dp[i][j] = min(dp[i][j], dp[i][k] + dp[k+1][j] + ((sum[j] - sum[k])%100)*((sum[k] - sum[i-1])%100));
    14             }
    15         }
    16     }
    17     printf("%d\n", dp[1][n]);
    18 }
    19 
    20 int main(){
    21     while(scanf("%d", &n) == 1){
    22         for(int i = 1; i <= n; i ++)
    23             for(int j = 1; j <= n; j ++)
    24                 dp[i][j] = (1 << 30);
    25         for(int i = 1; i <= n; i ++){
    26             scanf("%d", a + i);
    27             sum[i] = sum[i-1] + a[i];
    28             dp[i][i] = 0;
    29         }
    30         work();
    31     }
    32     return 0;
    33 }
    View Code

    (2)括号匹配

    这题解释括号匹配的例题,只要找到这个字符串中括号最大匹配量t,就可以得出答案,设长度为l,则ans = l - t;

    我们设dp[i][j] 为第i位到第j位最大的括号匹配量, 则他的转移方程为

    dp[i][j] = max(dp[i][j], dp[i][k] + dp[k+1][j]);

    当然如果第i位刚好与第j位刚好匹配

    则dp[i][j] = dp[i+1][j-1] + 2;

    下面附上代码

     1 #include <cstdio>
     2 #include <iostream>
     3 #include <cstring>
     4 #include <algorithm>
     5 using namespace std;
     6 
     7 string s;
     8 
     9 int n, dp[105][105];
    10 
    11 int main(){
    12     int T;
    13     scanf("%d", &T);
    14     while(T--){
    15         cin >> s;
    16         memset(dp, 0, sizeof(dp));
    17         for(int l = 0; l < s.size(); l ++){
    18             for(int i = 0; i + l < s.size(); i ++){
    19                 int j = i + l;
    20                 if(s[i] == '(' && s[j] == ')')
    21                    dp[i][j] = dp[i+1][j-1] + 2; 
    22                 if(s[i] == '[' && s[j] == ']')
    23                     dp[i][j] = dp[i+1][j-1] + 2;
    24                 for(int k = i; k <= j; k ++){
    25                     dp[i][j] = max(dp[i][j], dp[i][k] + dp[k+1][j]);
    26                 }
    27             }
    28         }
    29         printf("%d\n", s.size() - dp[0][s.size()-1]);
    30     }
    31     return 0;
    32 }
    View Code

    (3)整数划分

    当初一看到这一题的时候感觉像是搜索题,仔细一想才明白是一道区间dp题,既然是dp,当然要先找到状态了,设dp[i][j]为前i位中存在j个乘号

    我们以a[i][j]表示第i位到第j位的值,则可以推出状态转移方程为dp[i][j] = max(dp[i][j], dp[i][k] * a[k+1][j]);

    附上代码

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <algorithm>
     4 using namespace std;
     5 typedef long long ll;
     6 const int N = 100 + 5;
     7 
     8 ll a[N][N], dp[N][N];
     9 int n, T, c[N];
    10 char s[N];
    11 
    12 void work(){
    13     for(int j = 0; j < n; j ++){
    14         for(int i = 1; i <= strlen(s); i ++){
    15             for(int k = 1; k <= i; k ++){
    16                 if(j==0)
    17                     dp[i][0] = a[1][i];
    18                 else
    19                     dp[i][j] = max(dp[i][j], dp[k][j-1] * a[k+1][i]);
    20                 /*for(int p = 1; p <= strlen(s); p ++){
    21                     for(int q = 0; q < n; q ++)
    22                         printf("%d ", dp[p][q]);
    23                     puts("");
    24                 }*/
    25             }
    26         }
    27     }
    28     printf("%lld\n", dp[strlen(s)][n-1]);
    29 }
    30 
    31 int main(){
    32     scanf("%d", &T);
    33     while(T--){
    34         scanf("%s%d" , s, &n);
    35         int flag = 0;
    36         if(n > strlen(s)){
    37             printf("0\n");
    38             continue;
    39         }
    40         memset(a, 0, sizeof(a));
    41         memset(dp, 0, sizeof(dp));
    42         for(int i = 0; i < strlen(s); i ++)
    43             c[i+1] = s[i] - '0';
    44         for(int i = 1; i <= strlen(s); i ++){
    45             for(int j = i; j <= strlen(s); j ++){
    46                     a[i][j] = a[i][j-1] * 10 + c[i];
    47                 }
    48             }
    49         }
    50         /*for(int i = 1; i <= strlen(s); i ++){
    51             for(int j = i; j <= strlen(s); j ++)
    52                 printf("%I64d ", a[i][j]);
    53             puts("");
    54         }*/
    55         work();
    56     }
    57     return 0;
    58 }
    View Code
    既然要做,那就好好做! 自己选的路,自己走完!
  • 相关阅读:
    鼠标经过时背景颜色变化
    鼠标经过时弹出下拉菜单
    运行最新创建的镜像:
    docker 保存更改的镜像:
    docker 导入下载模板
    docker导入本地镜像
    docker 创建镜像
    Docker 基于已有镜像的容器创建镜像
    14.2.3 InnoDB Redo Log
    14.2.3 InnoDB Redo Log
  • 原文地址:https://www.cnblogs.com/zyf0163/p/4645923.html
Copyright © 2020-2023  润新知