【DP练习】区间DP

时间:2023-02-13 17:13:46

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便可。

【DP练习】区间DP【DP练习】区间DP
 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).

【DP练习】区间DP【DP练习】区间DP
 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\n", 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了好几发)

【DP练习】区间DP【DP练习】区间DP
 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("\n");
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)

【DP练习】区间DP【DP练习】区间DP
 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