黑书上的DP例题
page | section | no | title | submit |
113 | 1.5.1 | 例题1 | 括号序列 | POJ1141 |
116 | 1.5.1 | 例题2 | 棋盘分割 | POJ1191 |
117 | 1.5.1 | 例题3 | 决斗 | Sicily1822 |
117 | 1.5.1 | 例题4 | “舞蹈家”怀特先生 | ACM-ICPC Live Archive |
119 | 1.5.1 | 例题5 | 积木游戏 | http://202.120.80.191/problem.php?problemid=1244 |
123 | 1.5.2 | 例题1 | 方块消除 | http://poj.org/problem?id=1390 |
123 | 1.5.2 | 例题2 | 公路巡逻 | http://202.120.80.191/problem.php?problemid=1600 |
125 | 1.5.2 | 例题3 | 并行期望值 | POJ1074 |
131 | 1.5.2 | 例题6 | 不可分解的编码 | http://acmicpc-live-archive.uva.es/nuevoportal/data/problem.php?p=2475 |
133 | 1.5.2 | 例题7 | 青蛙的烦恼 | http://codewaysky.sinaapp.com/problem.php?id=1014 |
135 | 1.5.2 | 例题9 | 最优排序二叉树 | http://judge.noi.cn/problem?id=1059 |
138 | 1.5.2 | 例题10 | Bugs公司 | POJ1038 |
139 | 1.5.2 | 例题11 | 迷宫统计 | http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=70&page=show_problem&problem=1472 |
142 | 1.5.2 | 例题12 | 贪吃的九头龙 | http://judge.noi.cn/problem?id=1043 |
151 | 1.5.3 | 问题2 | 最长上升子序列问题 | http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=17&page=show_problem&problem=1475 |
151 | 1.5.3 | 问题3 | 最优二分检索树 | http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=15&page=show_problem&problem=1245 |
152 | 1.5.3 | 问题4 | 任务调度问题 | POJ1180 |
121 | 1.5.1 | 1.5.8 | 艺术馆的火灾 | http://221.192.240.23:9088/showproblem?problem_id=1366 |
144 | 1.5.2 | 1.5.10 | 快乐的蜜月 | http://judge.noi.cn/problem?id=1052 |
145 | 1.5.2 | 1.5.12 | 佳佳的筷子 | http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=14&page=show_problem&problem=1212 |
146 | 1.5.2 | 1.5.13 | 偷懒的工人 | POJ1337 |
146 | 1.5.2 | 1.5.15 | 平板涂色 | POJ1691 |
147 | 1.5.2 | 1.5.16 | 道路重建 | POJ1947 |
147 | 1.5.2 | 1.5.17 | 圆和多边形 | http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1679 |
148 | 1.5.2 | 1.5.18 | Jimmy落地 | POJ1661 |
148 | 1.5.2 | 1.5.19 | 免费糖果 | http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=13&page=show_problem&problem=1059 |
157 | 1.5.3 | 1.5.22 | 回文词 | POJ1159 |
157 | 1.5.3 | 1.5.24 | 邮局 | POJ1160 |
158 | 1.5.3 | 1.5.26 | 奶牛转圈 | POJ1946 |
158 | 1.5.3 | 1.5.27 | 元件折叠 | http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=14&page=show_problem&problem=1180 |
现在开始训练一下DP:
递归动机的DP:
pku 1141 Brackets Sequence
黑书上讲的第一个题目,题意就给出一个括号序列,包含有"(" ")" "[" "]" 求最少添加的括号数是的最终的序列是合法的并输出这个序列。
思路:
首先这里求解的话要使用递归的思路,这是动态规划产生的第一种动机,于是我们可以写出记忆化搜索。进行求解。
dp[l][r] = min(dp[l][r],dp[l + 1][r - 1])如果s[l]与s[r]匹配的话。
否则我们就将该序列分成两段分别求解(这也是求解DP线性模型的一种递归分解思路)
dp[l][r] = min(dp[l][r],dp[l][k] + dp[k + 1][r])
这里关键是怎么讲可行解输出呢?开始我是通过比较dp[l][r] 与 dp[l + 1][r -1] dp[l][k] + dp[k + 1][r]来判断的后来发现这样不对的 如果dp[l + 1][r - 1] = dp[l][k] + dp[k + 1][r]的话就会出现错误,而我们这里dp[l][r]确实从dp[l][k] + dp[k + 1][r]来的。所以,我们要另开一个二维数组记录每种最优状态的来源点。然后再递归的输出即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> #include <cmath> #include <vector> #include <cstring> #include <algorithm> #include <string> #include <set> #include <functional> #include <numeric> #include <sstream> #include <stack> #include <map> #include <queue> #define CL(arr, val) memset(arr, val, sizeof(arr)) #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define ll __int64 #define L(x) (x) << 1 #define R(x) (x) << 1 | 1 #define MID(l, r) (l + r) >> 1 #define Min(x, y) (x) < (y) ? (x) : (y) #define Max(x, y) (x) < (y) ? (y) : (x) #define E(x) (1 << (x)) #define iabs(x) (x) < 0 ? -(x) : (x) #define OUT(x) printf("%I64d\n", x) #define lowbit(x) (x)&(-x) #define Read() freopen("din.txt", "r", stdin) #define Write() freopen("dout.txt", "w", stdout); #define M 137 #define N 115 using namespace std; const int inf = 0x7f7f7f7f; const int mod = 1000000007; int dp[N][N]; char s[N]; int mk[N][N]; bool cmp(char a,char b) { if ((a == '(' && b == ')') || (a == '[' && b == ']')) return true; return false; } int dfs_DP(int l,int r) { int k; if (l > r) return 0; if (l == r) return (dp[l][r] = 1); if (dp[l][r] != inf) return dp[l][r]; if (cmp(s[l],s[r])) { if (l + 1 == r) { dp[l][r] = 0; mk[l][r] = -1; } else { dfs_DP(l + 1,r - 1); if (dp[l][r] > dp[l + 1][r - 1]) { dp[l][r] = dp[l + 1][r - 1]; mk[l][r] = -1; } // dp[l][r] = min(dp[l][r],dfs_DP(l + 1,r - 1)); // printf(">>>**%d %d %d %d\n",l,r,dp[l][r],dp[l + 1][r - 1]); } } for (k = l; k <= r - 1; ++k) { dfs_DP(l,k); dfs_DP(k + 1,r); if (dp[l][r] > dp[l][k] + dp[k + 1][r]) { dp[l][r] = dp[l][k] + dp[k + 1][r]; mk[l][r] = k; } // dp[l][r] = min(dp[l][r],dfs_DP(l,k) + dfs_DP(k + 1,r)); } return dp[l][r]; } void print(int l,int r) { if (l > r) return; if (l == r) { if (s[l] == '(' || s[l] == ')') printf("()"); else if (s[l] == '[' || s[l] == ']') printf("[]"); return; } if (cmp(s[l],s[r]) && mk[l][r] == -1) { printf("%c",s[l]); print(l + 1,r - 1); printf("%c",s[r]); } else { print(l,mk[l][r]); print(mk[l][r] + 1,r); } } int main() { // Read(); // Write(); int i,j; scanf("%s",s); int n = strlen(s); CL(mk,-1); for (i = 0; i <= n; ++i) { for (j = 0; j <= n; ++j) { dp[i][j] = inf; } } dfs_DP(0,n - 1); // printf("%d\n",dp[0][n - 1]); print(0,n - 1); printf("\n"); return 0; }
pku 1191 棋盘分割
思路:
棋盘横着切竖着切,然后递归将棋盘不断缩小到能够求解的状态。记忆化搜索。这里中间计算值可能会超0x7f7f7f7f,所以最大值取大一点。这里让哥条了很长时间。。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> #include <cmath> #include <vector> #include <cstring> #include <algorithm> #include <string> #include <set> #include <functional> #include <numeric> #include <sstream> #include <stack> #include <map> #include <queue> #define CL(arr, val) memset(arr, val, sizeof(arr)) #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define ll __int64 #define L(x) (x) << 1 #define R(x) (x) << 1 | 1 #define MID(l, r) (l + r) >> 1 #define Min(x, y) (x) < (y) ? (x) : (y) #define Max(x, y) (x) < (y) ? (y) : (x) #define E(x) (1 << (x)) #define iabs(x) (x) < 0 ? -(x) : (x) #define OUT(x) printf("%I64d\n", x) #define lowbit(x) (x)&(-x) #define Read() freopen("din.txt", "r", stdin) #define Write() freopen("dout.txt", "w", stdout); #define M 137 #define N 10 using namespace std; const int inf = 0x7f7f7f7f; const int mod = 1000000007; int mat[N][N]; int s[N][N][N][N]; int dp[N][N][N][N][20]; ll map[N][N]; int n; int DP(int x1,int y1,int x2,int y2,int no) { int x,y; if (no == 1) return dp[x1][y1][x2][y2][no]; if (dp[x1][y1][x2][y2][no] != -1) return dp[x1][y1][x2][y2][no]; int ans = 999999999; for (x = x1; x < x2; ++x) { ans = min(ans,min(DP(x1,y1,x,y2,no - 1) + s[x + 1][y1][x2][y2],DP(x + 1,y1,x2,y2,no - 1) + s[x1][y1][x][y2])); } for (y = y1; y < y2; ++y) { ans = min(ans,min(DP(x1,y1,x2,y,no - 1) + s[x1][y + 1][x2][y2],DP(x1,y + 1,x2,y2,no - 1) + s[x1][y1][x2][y])); } return (dp[x1][y1][x2][y2][no] = ans); } int main() { // Read(); int i,j; scanf("%d",&n); int sum = 0; for (i = 1; i <= 8; ++i) { for (j = 1; j <= 8; ++j) { scanf("%d",&mat[i][j]); sum += mat[i][j]; } } CL(s,0); CL(dp,-1); int x1,y1,x2,y2; for (x1 = 1; x1 <= 8; ++x1) { for (y1 = 1; y1 <= 8; ++y1) { for (x2 = x1; x2 <= 8; ++x2) { for (y2 = y1; y2 <= 8; ++y2) { // printf("%d %d %d %d\n",x1,y1,x2,y2); for (i = x1; i <= x2; ++i) { for (j = y1; j <= y2; ++j) { s[x1][y1][x2][y2] += mat[i][j]; } } s[x1][y1][x2][y2] *= s[x1][y1][x2][y2]; dp[x1][y1][x2][y2][1] = s[x1][y1][x2][y2]; } } } } DP(1,1,8,8,n); double ans = (1.0*dp[1][1][8][8][n])/(1.0*n) - (1.0*sum*sum)/(1.0*n*n); printf("%.3f\n",sqrt(ans)); return 0; }
sicily 1822 Fight Club
题意:黑书
思路:
开始吧题意理解错了,一位如果判断i必须从i开始一次与i + 1,i +2比较呢。SB了。。
dp[i][j]表示i能否与j相遇,记住这里是相遇不表示i与j谁能打败谁。如果判断x点,我们把x点查分为x与n + 1,这样讲原来的环转化成连,然后求x与n +1是否能够相遇即可
dp[i][j] = (dp[i][k] && dp[k][j] && (mat[i][k],mat[j][k])),mat[i][j]表示i能否打败j
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> #include <cmath> #include <vector> #include <cstring> #include <algorithm> #include <string> #include <set> #include <functional> #include <numeric> #include <sstream> #include <stack> #include <map> #include <queue> #define CL(arr, val) memset(arr, val, sizeof(arr)) #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define ll __int64 #define L(x) (x) << 1 #define R(x) (x) << 1 | 1 #define MID(l, r) (l + r) >> 1 #define Min(x, y) (x) < (y) ? (x) : (y) #define Max(x, y) (x) < (y) ? (y) : (x) #define E(x) (1 << (x)) #define iabs(x) (x) < 0 ? -(x) : (x) #define OUT(x) printf("%I64d\n", x) #define lowbit(x) (x)&(-x) #define Read() freopen("din.txt", "r", stdin) #define Write() freopen("dout.txt", "w", stdout); #define M 137 #define N 50 using namespace std; const int inf = 0x7f7f7f7f; const int mod = 1000000007; int mat[N][N]; int dp[N][N]; int seq[N]; bool vt[N][N]; int n; int DP(int l,int r) { int k; if (vt[l][r]) return dp[l][r]; for (k = l + 1; k <= r - 1; ++k) { dp[l][r] = (DP(l,k)&&DP(k,r)&&(mat[seq[l]][seq[k]] || mat[seq[r]][seq[k]])); if (dp[l][r]) break; } vt[l][r] = true; return dp[l][r]; } int main() { // Read(); int T,i,j; scanf("%d",&T); while (T--) { scanf("%d",&n); CL(mat,0); for (i = 1; i <= n; ++i) for (j = 1; j <= n; ++j) scanf("%d",&mat[i][j]); for (i = 1; i <= n; ++i) { CL(dp,0); CL(vt,false); int pos = (i - 1); if (pos == 0) pos = n; int ln = 0; for (j = i; j <= n; ++j) seq[++ln] = j; for (j = 1; j <= i - 1; ++j) seq[++ln] = j; seq[++ln] = n + 1; for (j = 1; j <= n; ++j) { dp[j][j + 1] = dp[j + 1][j] = 1; vt[j][j + 1] = vt[j + 1][j] = true; } DP(1,ln); if (dp[1][ln]) printf("1\n"); else printf("0\n"); } // if (T != 0) printf("\n"); } return 0; }
hdu 3632 A Captivating Match
题意:
这题和上题目意思一样,只不过这里是序列1-n然后每个人可以选择左右两边的人进行对决,我刚开始看到这题目的时候,就以为是上边那道题目,结果样例过了,就是不对。
思路:
这里某个人i如果能够胜出,那么他一定能够和0点并且和n + 1点相遇。(与上题一样,抽象出来的两个点)。然后我们只要枚举任意两点是否能够相遇即可,
dp[i][j] = (dp[i][k] && dp[k][j] && (mat[i][k],mat[j][k])),mat[i][j]表示i能否打败j, 这里根据i到j的距离进行递推的。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> #include <cmath> #include <vector> #include <cstring> #include <algorithm> #include <string> #include <set> #include <functional> #include <numeric> #include <sstream> #include <stack> #include <map> #include <queue> #define CL(arr, val) memset(arr, val, sizeof(arr)) #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define ll __int64 #define L(x) (x) << 1 #define R(x) (x) << 1 | 1 #define MID(l, r) (l + r) >> 1 #define Min(x, y) (x) < (y) ? (x) : (y) #define Max(x, y) (x) < (y) ? (y) : (x) #define E(x) (1 << (x)) #define iabs(x) (x) < 0 ? -(x) : (x) #define OUT(x) printf("%I64d\n", x) #define lowbit(x) (x)&(-x) #define Read() freopen("din.txt", "r", stdin) #define Write() freopen("dout.txt", "w", stdout); #define M 30007 #define N 117 using namespace std; const int inf = 100000007; const int mod = 1000000007; int val[N]; int mat[N][N]; int n; int dp[N][N]; bool vt[N][N]; bool isok(int i,int j,int k) { if (dp[i][k] && dp[k][j] && (mat[i][k] || mat[j][k])) return true; else return false; } int main() { // Read(); int T,i,j; int cas = 1; scanf("%d",&T); while (T--) { scanf("%d",&n); for (i = 1; i <= n; ++i) scanf("%d",&val[i]); CL(mat,0);//记住要初始化 for (i = 1; i <= n; ++i) for (j = 1; j <= n; ++j) scanf("%d",&mat[i][j]); CL(dp,0); for (i = 0; i <= n + 1; ++i) dp[i][i + 1] = dp[i + 1][i] = 1; int k,p; for (k = 2; k <= n; ++k)//递推策略 { for (i = 0; i + k <= n + 1; ++i) { j = i + k; for (p = i + 1; p <= j - 1; ++p) { if (isok(i,j,p)) { dp[i][j] = 1; } } } } int ans = 0; for (i = 1; i <= n; ++i) { if (dp[0][i] && dp[i][n + 1] && val[i] > ans) ans = val[i]; //i能否杀到0并且能够杀到n + 1 } printf("Case %d: %d\n",cas++,ans); } return 0; }
题意:看给书吧..
思路:
我们考虑的是最后一段是单独消去还是和前边的一起消去,这样就可以构造出递归的递推式了。
题目的方块可以表示称color[i],len[i],1<=i<=l
这里l表示有多少"段"不同的颜色方块
color[i]表示第i段的颜色,len[i]表示第i段的方块长度
让f[i,j,k]表示把(color[i],len[i]),(color[i+1],len[i+1]),...,(color[j-1],len[j-1]),(color[j],len[j]+k)合并的最大得分
考虑(color[j],len[j]+k)这一段,要不马上消掉,要不和前面的若干段一起消掉
1.如果马上消掉,就是f[i,j-1,0]+(len[j]+k)^2
2.如果和前面的若干段一起消,可以假设这"若干段"中最后一段是p,则此时的得分是f[i,p,k+len[j]]+f[p+1,j-1,0]
于是f[i,j,k] = max{ f[i,j-1,0]+(len[j]+k)^2, f[i,p,k+len[j]]+f[p+1,j-1,0]
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> #include <cmath> #include <vector> #include <cstring> #include <algorithm> #include <string> #include <set> #include <functional> #include <numeric> #include <sstream> #include <stack> #include <map> #include <queue> #include <utility> #define CL(arr, val) memset(arr, val, sizeof(arr)) #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define ll long long #define L(x) (x) << 1 #define R(x) (x) << 1 | 1 #define MID(l, r) (l + r) >> 1 #define Min(x, y) (x) < (y) ? (x) : (y) #define Max(x, y) (x) < (y) ? (y) : (x) #define E(x) (1 << (x)) #define iabs(x) (x) < 0 ? -(x) : (x) #define OUT(x) printf("%I64d\n", x) #define lowbit(x) (x)&(-x) #define Read() freopen("din.txt", "r", stdin) #define Write() freopen("dout.txt", "w", stdout); #define M 137 #define N 207 using namespace std; const int inf = 0x7f7fffff; const ll mod = 1000000007; int dp[N][N][N]; int a[N],of[N],b[N]; int n,ln; int q_DP(int l,int r,int k) { int p; if (dp[l][r][k] != 0) return dp[l][r][k]; if (l == r) { return (dp[l][r][k] = (b[r] + k)*(b[r] + k)); } int ans = 0; ans = max(ans,q_DP(l,r - 1,0) + (b[r] + k)*(b[r] + k)); for (p = l; p + 1 <= r - 1; ++p) { if (of[r] == of[p]) ans = max(ans,q_DP(l,p,k + b[r]) + q_DP(p + 1,r - 1,0)); } return dp[l][r][k] = ans; } int main() { // Read(); int i; int T,cas = 1; scanf("%d",&T); while (T--) { scanf("%d",&n); ln = 0; CL(a,0); for (i = 0; i < n; ++i) { scanf("%d",&a[i]); } ln = 0; int ct = 0; for (i = 0; i < n; ++i) { if (a[i] != a[i + 1]) { b[++ln] = ct + 1; of[ln] = a[i]; ct = 0; } else ct++; } // b[++ln] = ct; // of[ln] = a[n - 1]; CL(dp,0); q_DP(1,ln,0); printf("Case %d: %d\n",cas++,dp[1][ln][0]); } return 0; }
多决策动机的DP:
题意:黑书。。
思路:
这里每一步要么走左腿,要么走右腿,要么原地不动。DFS求解肯定超时,因为状态数太多,所以我们只好利用DP记录所有状态,然后通过每一步的决策。求解所有满足条件的状态。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> #include <cmath> #include <vector> #include <cstring> #include <algorithm> #include <string> #include <set> #include <functional> #include <numeric> #include <sstream> #include <stack> #include <map> #include <queue> #define CL(arr, val) memset(arr, val, sizeof(arr)) #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define ll __int64 #define L(x) (x) << 1 #define R(x) (x) << 1 | 1 #define MID(l, r) (l + r) >> 1 #define Min(x, y) (x) < (y) ? (x) : (y) #define Max(x, y) (x) < (y) ? (y) : (x) #define E(x) (1 << (x)) #define iabs(x) (x) < 0 ? -(x) : (x) #define OUT(x) printf("%I64d\n", x) #define lowbit(x) (x)&(-x) #define Read() freopen("din.txt", "r", stdin) #define Write() freopen("dout.txt", "w", stdout); #define M 137 #define N 10017 using namespace std; const int inf = 0x7f7fffff; const int mod = 1000000007; int a[N]; int dp[5][5][3]; int getS(int x,int y) { if (x == 0) return 2; else if (x == y) return 1; else if (abs(x - y) == 2) return 4; else return 3; } int main() { // Read(); int i,j,k; int n; a[0] = 0; while (~scanf("%d",&a[1])) { if (a[1] == 0) break; i = 2; while (scanf("%d",&a[i])) { if (a[i] == 0) break; i++; } n = i - 1; for (i = 0; i <= 4; ++i) { for (j = 0; j <= 4; ++j) { for (k = 0; k <= 1; ++k) dp[i][j][k] = inf; } } dp[0][0][0] = 0; int u = 0, v = 1; for (k = 0; k < n; ++k) { for (i = 0; i <= 4; ++i) { for (j = 0; j <= 4; ++j) { if (dp[i][j][u] != inf) { if (a[k + 1] != j) //保证左右脚不能在一起 dp[a[k + 1]][j][v] = min(dp[a[k + 1]][j][v],dp[i][j][u] + getS(i,a[k + 1])); if (i != a[k + 1]) dp[i][a[k + 1]][v] = min(dp[i][a[k + 1]][v],dp[i][j][u] + getS(j,a[k + 1])); } } } swap(u,v); //滚动数组优化 for (i = 0; i <= 4; ++i) { for (j = 0; j <= 4; ++j) { dp[i][j][v] = inf; } } } int ans = inf; for (i = 0; i <= 4; ++i) { if (i != a[n]) ans = min(dp[i][a[n]][u],ans); } for (j = 0; j <= 4; ++j) { if (j != a[n]) ans = min(dp[a[n]][j][u],ans); } printf("%d\n",ans); } return 0; }
题意:。。。
思路:
就是按着黑书上的第一种思路做的,这里有个地方卡了一下, 就是j表示的类成了j堆,在枚举的时候不能只到m-1否则最后累到第m的堆的最后一个也就不存在了,。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> #include <cmath> #include <vector> #include <cstring> #include <algorithm> #include <string> #include <set> #include <functional> #include <numeric> #include <sstream> #include <stack> #include <map> #include <queue> #include <utility> #define CL(arr, val) memset(arr, val, sizeof(arr)) #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define ll long long #define L(x) (x) << 1 #define R(x) (x) << 1 | 1 #define MID(l, r) (l + r) >> 1 #define Min(x, y) (x) < (y) ? (x) : (y) #define Max(x, y) (x) < (y) ? (y) : (x) #define E(x) (1 << (x)) #define iabs(x) (x) < 0 ? -(x) : (x) #define OUT(x) printf("%I64d\n", x) #define lowbit(x) (x)&(-x) #define Read() freopen("din.txt", "r", stdin) #define Write() freopen("dout.txt", "w", stdout); #define M 137 #define N 107 using namespace std; const int inf = 0x7f7fffff; const ll mod = 1000000007; int dp[N][N][N][4]; bool vt[N][N][N][4]; int a[N][4][4]; int n,m; bool over(int i,int x,int j,int y) { int sda = max(a[i][x][0],a[i][x][1]); int sdb = min(a[i][x][0],a[i][x][1]); int pda = max(a[j][y][0],a[j][y][1]); int pdb = min(a[j][y][0],a[j][y][1]); if (sda <= pda && sdb <= pdb) return true; else return false; } int main() { // Read(); int i,j,k,p,q; int x,y,z; scanf("%d%d",&n,&m); CL(a,0); for (i = 1; i <= n; ++i) { scanf("%d%d%d",&x,&y,&z); //表示第i个面的的长宽高 a[i][0][0] = x; a[i][0][1] = y; a[i][0][2] = z; a[i][1][0] = y; a[i][1][1] = z; a[i][1][2] = x; a[i][2][0] = z; a[i][2][1] = x; a[i][2][2] = y; } CL(dp,0); CL(vt,false); vt[0][0][0][0] = true; for (i = 0; i < n; ++i) { for (j = 0; j <= min(i,m); ++j) { for (k = 0; k <= i; ++k) { for (p = 0; p < 3; ++p) { if (vt[i][j][k][p]) { for (q = 0; q < 3; ++q) { //可以累加 if (over(i + 1,q,k,p)) { dp[i + 1][j][i + 1][q] = max(dp[i + 1][j][i + 1][q],dp[i][j][k][p] + a[i + 1][q][2]); vt[i + 1][j][i + 1][q] = true; } //另起一堆 dp[i + 1][j + 1][i + 1][q] = max(dp[i + 1][j + 1][i + 1][q],dp[i][j][k][p] + a[i + 1][q][2]); vt[i + 1][j + 1][i + 1][q] = true; //直接跳过 dp[i + 1][j][k][p] = max(dp[i + 1][j][k][p],dp[i][j][k][p]); vt[i + 1][j][k][p] = true; // printf(">>>>%d %d %d\n",dp[i + 1][j][i + 1][q],dp[i + 1][j + 1][i + 1][q],dp[i + 1][j][k][p]); } } } } } } int ans = 0; // printf("%d %d\n",n,m); for (k = 1; k <= n; ++k) { for (p = 0; p < 3; ++p) { // printf(">>>>>>%d\n",dp[n][m][k][p]); ans = max(ans,dp[n][m][k][p]); } } printf("%d\n",ans); return 0; }
思路:
dp[i][j]表示到达在时间为j时第i个关口与巡逻车相遇的最少次数 dp[i + 1][j + 1] = max(dp[i + 1][j + 1],dp[i][j] + s); s表示目标车在 [j, j + k]时间段内从第i个关口出发到i + 1个关口与巡逻车相遇的次数。
其实状态转移很好想,只是在车辆相遇上脑子短路了,我们只要排除不相遇的就好了。
这里还没有优化。。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> #include <cmath> #include <vector> #include <cstring> #include <algorithm> #include <string> #include <set> #include <functional> #include <numeric> #include <sstream> #include <stack> #include <map> #include <queue> #define CL(arr, val) memset(arr, val, sizeof(arr)) #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define ll long long #define L(x) (x) << 1 #define R(x) (x) << 1 | 1 #define MID(l, r) (l + r) >> 1 #define Min(x, y) (x) < (y) ? (x) : (y) #define Max(x, y) (x) < (y) ? (y) : (x) #define E(x) (1 << (x)) #define iabs(x) (x) < 0 ? -(x) : (x) #define OUT(x) printf("%I64d\n", x) #define lowbit(x) (x)&(-x) #define Read() freopen("din", "r", stdin) #define Write() freopen("dout", "w", stdout); #define M 86405 #define N 55 using namespace std; const int inf = 0x7f7f7f7f; const int mod = 1000000007; struct node { int s,e; }nd[N][N]; int ln[N]; int dp[N][M]; bool vt[N][M]; int n,m; int main() { // Read(); int i,j,k; int ni,di; char ti[7]; scanf("%d%d",&n,&m); CL(ln,0); CL(nd,0); for (j = 0; j < m; ++j) { scanf("%d%s%d",&ni,ti,&di); int no = 0; int sum = 0; for (i = 0; i < 6; ++i) { if (i%2 == 1) { no = no*10 + ti[i] - '0'; int tmp = 0; if (i == 1) tmp = 3600; else if (i == 3) tmp = 60; else tmp = 1; sum += no*tmp; no = 0; } else { no = no*10 + ti[i] - '0'; } } int &p = ln[ni]; nd[ni][p].s = sum; nd[ni][p].e = sum + di; p++; } for (i = 1; i <= n; ++i) { for (j = 0; j <= 86400; ++j) { dp[i][j] = inf; } } CL(vt,false); dp[1][21600] = 0; vt[1][21600] = true; for (i = 1; i < n; ++i) { for (j = 21600; j <= 51000; ++j) { if (vt[i][j]) { for (k = 300; k <= 600; ++k) { int s = 0; int p; for (p = 0; p < ln[i]; ++p) { if (nd[i][p].e == j + k) s++; else { if (j <= nd[i][p].s && j + k <= nd[i][p].e) continue; if (j >= nd[i][p].s && j + k >= nd[i][p].e) continue; s++; } } dp[i + 1][j + k] = min(dp[i + 1][j + k],dp[i][j] + s); vt[i + 1][j + k] = true; } } } } int ans = inf; int tim = 0; for (i = 21600; i <= 51000; ++i) { if (vt[n][i] && dp[n][i] < ans) { ans = dp[n][i]; tim = i; } } int hh = tim/3600; tim -= hh*3600; int mm = tim/60; tim -= mm*60; int ss = tim; printf("%d\n%02d%02d%02d\n",ans,hh,mm,ss); return 0; }
===================================================================
poj 1337 A Lazy Worker
题意:
一个懒工人,他想工作的时间尽量少。有N个任务,每个任务有开始时间和deadline,工人完成这个工作需要ti时间。如果某个时刻有工作可以做(这里是说,如果从这个时刻开始某个工作,在deadline之前能够完成),那么这个工人必须做,但是如果这个时刻存在着多余1件工作可以做,工人可以选择;假设这个时刻没有工作可以做了,工人就可以偷懒直到有新的任务到来
思路:
刚开始以为只要我一空下来有工作,我就必须从这一点开始工作来着,其实不是的,只要在最晚期限之前完成该工作即可。
dp[i]表示在时间点i时,完成工作所需要的最少时间,dp[i + 1] = min(dp[i + 1],dp[i])当该时间没有工作干时,当有多个工作时,dp[i + tk] = min(dp[i + tk],dp[i] + tk) k表示第k个工作可以再时间点i完成
这里注意inf = 0x3fffffff 因为会有inf的相加,这里快整死我了,给很长时间没有条出来。。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> #include <cmath> #include <vector> #include <cstring> #include <algorithm> #include <string> #include <set> #include <functional> #include <numeric> #include <sstream> #include <stack> #include <map> #include <queue> #include <list> #define CL(arr, val) memset(arr, val, sizeof(arr)) #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define ll long long #define L(x) (x) << 1 #define R(x) (x) << 1 | 1 #define MID(l, r) (l + r) >> 1 #define Min(x, y) (x) < (y) ? (x) : (y) #define Max(x, y) (x) < (y) ? (y) : (x) #define E(x) (1 << (x)) #define iabs(x) (x) < 0 ? -(x) : (x) #define OUT(x) printf("%I64d\n", x) #define lowbit(x) (x)&(-x) #define Read() freopen("din.txt", "r", stdin) #define Write() freopen("dout.txt", "w", stdout); #define M 107 #define N 117 using namespace std; const ll mod = 1000000007; const int inf = 0x3fffffff; struct node { int ti,ai,di; }nd[N]; int dp[260]; vector<int> work[260]; int main() { // Read(); // printf("%d \n%d\n",0x7fffffff,0x3fffffff); int T; int i,j; int n; scanf("%d",&T); while (T--) { scanf("%d",&n); int s = inf, e = 0; for (i = 0; i < n; ++i) { scanf("%d%d%d",&nd[i].ti,&nd[i].ai,&nd[i].di); s = min(s,nd[i].ai); e = max(e,nd[i].di); } for (i = s; i <= e; ++i) { work[i].clear(); for (j = 0; j < n; ++j) { if (i >= nd[j].ai && i + nd[j].ti <= nd[j].di) { work[i].push_back(j); } } } for (i = s; i <= e; ++i) dp[i] = inf; dp[s] = 0; for (i = s; i < e; ++i) { if (work[i].size() == 0) dp[i + 1] = min(dp[i + 1],dp[i]); else { int sz = work[i].size(); for (int j = 0; j < sz; ++j) { int k = work[i][j]; dp[i + nd[k].ti] = min(dp[i + nd[k].ti],dp[i] + nd[k].ti); } } } printf("%d\n",dp[e]); } return 0; }