【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\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了好几发)

 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)

 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

 

你可能感兴趣的:(dp)