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 }
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 }
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 }
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 }