• 【DP练习】区间DP


    1、LightOJ 1422 Halloween Costumes

    题目链接:http://lightoj.com/volume_showproblem.php?problem=1422

    题意:gappu要参加n场万圣节晚会,每场他都要cosplay,所穿的衣服可以叠加穿在身上,但是一旦脱掉就不会再穿,给出每场晚会要穿的衣服编号,后面的场次服装可以与前面的相同,问你他最少需要消耗多少件衣服。

    思路:dp[i][j]表示从第i场到第j场晚会最少穿多少件衣服,如果[i+1, j]都没有服装与第i场的一样,那么dp[i][j] = dp[i+1][j] + 1. 如果[i+1, j]中的第k场的服装与当前i的相同,则dp[i][j] = min(dp[i+1][k-1]+dp[k][j], dp[i][j]); 其中dp[i+1][k-1] 表示第i场的服装一直穿着遇到第k件的时候脱掉所有[i+1, k-1]件。每次枚举k便可。

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <algorithm>
     5 using namespace std;
     6 int dp[1000][1000];
     7 int a[300];
     8 int main()
     9 {
    10     int T;
    11     scanf("%d", &T);
    12     for(int cas = 1; cas <= T; cas ++) {
    13         int n;
    14         scanf("%d", &n);
    15         memset(dp, 0, sizeof(dp));
    16         for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
    17         for(int i = 1; i <= n; i ++) dp[i][i] = 1;
    18 
    19 
    20         for(int i = n-1; i > 0; i --) {
    21             for(int j = i+1; j <= n; j ++) {
    22                 dp[i][j] = dp[i+1][j] + 1;
    23                 for(int k = i+1; k <= j; k ++)
    24                     if(a[i] == a[k])
    25                         dp[i][j] = min(dp[i+1][k-1]+dp[k][j], dp[i][j]);
    26             }
    27         }
    28         cout <<"Case "<<cas<<": "<< dp[1][n] << endl;
    29     }
    30 }
    View Code

     2、POJ 2955 Brackets

    题目链接:http://poj.org/problem?id=2955

    题意:求括号的最大匹配数。

    思路:dp[i][j] 表示从i到j最大的匹配数,每次循环开始预设所有的括号都不匹配,则dp[i][j] = dp[i+1][j]; 在[i+1, j] 枚举k,当char[k] == char[i]时,dp[i][j] = max(dp[i][j], dp[i+1][k-1]+dp[k][j] + 2).

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <string>
     5 using namespace std;
     6 int main()
     7 {
     8     char arr[200];
     9     while(1){
    10         scanf("%s", arr);
    11         if(arr[0] == 'e') break;
    12         int len = strlen(arr);
    13         int dp[200][200] = {0};
    14         for(int i = len-1; i >= 0; i--) {
    15             for(int j = i+1; j < len; j++) {
    16                 dp[i][j] = dp[i+1][j];
    17                 char x = arr[i];
    18                 for(int k = i+1; k <= j; k++) {
    19                     if((x == '(' && arr[k] == ')') || (x == '[' && arr[k] == ']')){
    20                         dp[i][j] = max(dp[i][j], dp[i+1][k-1]+dp[k][j] + 2);
    21                     }
    22                 }
    23             }
    24         }
    25         printf("%d
    ", dp[0][len-1]);
    26 
    27     }
    28     return 0;
    29 }
    View Code

    3、POJ 1141Brackets Sequence(路径还原)

    题意:括号的最少补全。输出补全后序列。

    思路:DP路径还原 。rel[i][j]表示[i, j]中与arr[i]补全的位置,初始化为-1。dp[i][j] 表示[i, j]中最小的补全数,最初假设arr[i]不匹配,则dp[i][j]=dp[i+1][j]+1;然后每次枚举i < k <= j, 当arr[i]与arr[k]匹配dp[i][j] =min( dp[i+1][k-1]+dp[k][j]-1, dp[i][j]), rel记录转移;最后递归恢复路径。(因为答案不止一个,非递归恢复写得比较脑残然后WA了好几发)

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <string>
     5 #include <algorithm>
     6 using namespace std;
     7 int dp[300][300], rel[300][300];
     8 char arr[300];
     9 bool vis[300];
    10 void mark(int i, int j)
    11 {
    12     if(i >= j) return ;
    13     if(rel[i][j] == -1) mark(i+1, j);
    14     else {
    15         vis[i] = vis[rel[i][j]] = 1;
    16         mark(i+1, rel[i][j]-1);
    17         mark(rel[i][j], j);
    18     }
    19 }
    20 int main()
    21 {
    22     while(gets(arr) > 0){
    23         memset(dp, 0, sizeof(dp));
    24         memset(rel, -1, sizeof(rel));
    25         memset(vis, 0, sizeof(vis));
    26 
    27         int len = strlen(arr);
    28         for(int i  = 0; i < len; i++) dp[i][i] = 1;
    29         for(int i = len-1; i >= 0; i--) {
    30             for(int j = i+1; j < len; j++) {
    31                 dp[i][j] = dp[i+1][j] + 1;
    32                 rel[i][j] = -1;
    33                 for(int k = i+1; k <= j; k++) {
    34                     if((arr[i] == '(' && arr[k] == ')') || (arr[i] == '[' && arr[k] == ']')) {
    35                         if(dp[i][j] > dp[i+1][k-1] + dp[k][j] - 1) {
    36                             dp[i][j] = dp[i+1][k-1] + dp[k][j] - 1;
    37                             rel[i][j] = k;
    38                         }
    39                     }
    40                 }
    41             }
    42         }
    43         mark(0, len-1);
    44         for(int i = 0; i < len; i++) {
    45             if(vis[i]) printf("%c", arr[i]) ;
    46             else {
    47                 if(arr[i] == '(' || arr[i] == ')') printf("()");
    48                 else printf("[]");
    49             }
    50         }printf("
    ");
    51     }
    52     return 0;
    53 }
    View Code

    4、POJ3280 Cheapest Palindrome

    题意:给你一个字符串,通过删除字符和增加字符构造成回文串,删除和增加都有相应的代价。问构造回文串的最小代价。

    思路:dp[i][j] 代表[i, j]是回文串时的最小代价。则有三种转移情况:1)假设[i+1, j]是回文,增/删s[i]的代价,dp[i][j] = min(dp[i+1][j]+add_cost[i], dp[i+1][j]+del_cost[i]);2)dp[i][j] = min(dp[i][j-1]+add_cost[j], dp[i][j-1]+del_cost[j]);3)当s[i] == s[j]时, dp[i][j] = min(dp[i][j], d[i+1][j-1]);

    (一直不知道自己WA的7、8次到底是为什么,重写了一遍就过了Orz)

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <string>
     5 using namespace std;
     6 int dp[2005][2005];
     7 char s[2005];
     8 int add[30], del[30];
     9 int main()
    10 {
    11     int n, m;
    12     while(scanf("%d%d", &n, &m) != EOF) {
    13         scanf("%s", s);
    14         for(int i = 0; i < n; i ++) {
    15             getchar();
    16             char c; cin >> c;
    17             scanf("%d%d", &add[c-'a'], &del[c-'a']);
    18         }
    19         memset(dp, 0, sizeof(dp));
    20         for(int i = m-2; i >= 0; i--){
    21             for(int j = i+1; j < m; j++) {
    22                 dp[i][j] = min(dp[i+1][j]+add[s[i]-'a'], dp[i+1][j]+del[s[i]-'a']);
    23                 int b = min(dp[i][j-1]+add[s[j]-'a'], dp[i][j-1]+del[s[j]-'a']);
    24                 dp[i][j] = min(b, dp[i][j]);
    25                 if(s[i] == s[j]) dp[i][j] = min(dp[i][j], dp[i+1][j-1]);
    26             }
    27         }
    28         cout << dp[0][m-1] << endl;
    29     }
    30 }
    View Code
  • 相关阅读:
    求欧拉回路的算法学习
    2020牛客暑期多校训练营(第六场 )C Combination of Physics and Maths(思维)
    2020牛客暑期多校训练营(第六场)E.Easy Construction(思维构造,附图解)
    CF1038D Slime(思维+枚举+贪心)(来自洛谷)
    CF1250B The Feast and the Bus(贪心+枚举)(来自洛谷)
    Codeforces Round #659 (Div. 2) A.Common Prefixes
    IDEA本人亲测可用的破解方法
    Codeforces Round #658 (Div. 2)(A,B博弈,C1,C2)
    2020牛客暑期多校训练营(第四场)B.Basic Gcd Problem(数学)
    2020牛客暑期多校训练营(第三场)B.Classical String Problem(思维)
  • 原文地址:https://www.cnblogs.com/ZiningTang/p/4402420.html
Copyright © 2020-2023  润新知