BZOJ 4831 [DP]
2017-04-28 20:36
148 查看
Description
给定一个长度为 n 的非负整数序列 a_1,a_2,…a_n 。你可以使用一种操作:选择在序列中连续的两个正整数,并使它们分别减一。当你不能继续操作时游戏结束,而你的得分等于你使用的操作次数。你的任务是计算可能的最小得分和最大得分。Solution
太强辣,感觉细节比较多。
#include <cstdio> #include <cstring> #include <iostream> using namespace std; const int N = 1010101; const int INF = 1 << 30; inline char get(void) { static char buf[100000], *S = buf, *T = buf; if (S == T) { T = (S = buf) + fread(buf, 1, 100000, stdin); if (S == T) return EOF; } return *S++; } inline void read(int &x) { static char c; x = 0; for (c = get(); c < '0' || c > '9'; c = get()); for (; c >= '0' && c <= '9'; c = get()) x = x * 10 + c - '0'; } int n, test, suf, ans1, ans2; int a ; int f [2][2], g [2][2]; inline int Min(int a, int b) { return a < b ? a : b; } inline int Max(int a, int b) { return a > b ? a : b; } int main(void) { read(test); memset(f, 0x3f, sizeof f); while (test--) { read(n); for (int i = 1; i <= n; i++) read(a[i]); if (a[1] <= a[2]) { for (int i = 0; i < a[1]; i++) f[a[2] - i][0][1] = i; f[a[2] - a[1]][0][0] = a[1]; } else { for (int i = 0; i <= a[2]; i++) f[a[2] - i][0][1] = i; } for (int i = 2; i < n; i++) { for (int s = 0; s < 2; s++) for (int j = 0; j <= a[i + 1]; j++) f[j][i & 1 ^ 1][s] = INF; for (int s = 0; s < 2; s++) { for (int j = 0; j <= a[i]; j++) { if (j <= a[i + 1]) f[a[i + 1] - j][i & 1 ^ 1][0] = Min(f[a[i + 1] - j][i & 1 ^ 1][0], f[j][i & 1][s] + j); else f[0][i & 1 ^ 1][1] = Min(f[0][i & 1 ^ 1][1], f[j][i & 1][0] + a[i + 1]); } if (s == 0) { suf = INF; for (int k = a[i]; k > Min(a[i], a[i + 1]); k--) suf = Min(suf, f[k][i & 1][0]); for (int k = Min(a[i], a[i + 1]); k >= 0; k--) { f[a[i + 1] - k][i & 1 ^ 1][1] = Min(f[a[i + 1] - k][i & 1 ^ 1][1], suf + k); suf = Min(suf, f[k][i & 1][0]); } } } for (int s = 0; s < 2; s++) for (int j = 0; j <= a[i]; j++) f[j][i & 1][s] = INF; } ans1 = INF; for (int s = 0; s < 2; s++) for (int i = 0; i <= a ; i++) ans1 = Min(ans1, Min(f[0][n & 1][s], f[i][n & 1][0])); for (int s = 0; s < 2; s++) for (int i = 0; i <= a ; i++) f[i][n & 1][s] = INF; if (a[1] <= a[2]) { for (int i = 0; i < a[1]; i++) g[a[2] - i][0][1] = i; g[a[2] - a[1]][0][0] = a[1]; } else { for (int i = 0; i <= a[2]; i++) g[a[2] - i][0][1] = i; } for (int i = 2; i < n; i++) { for (int s = 0; s < 2; s++) for (int j = 0; j <= a[i + 1]; j++) g[j][i & 1 ^ 1][s] = 0; for (int s = 0; s < 2; s++) { for (int j = 0; j <= a[i]; j++) { if (j <= a[i + 1]) g[a[i + 1] - j][i & 1 ^ 1][0] = Max(g[a[i + 1] - j][i & 1 ^ 1][0], g[j][i & 1][s] + j); else g[0][i & 1 ^ 1][1] = Max(g[0][i & 1 ^ 1][1], g[j][i & 1][0] + a[i + 1]); } if (s == 0) { suf = 0; for (int k = Min(a[i], a[i + 1]); k >= 0; k--) { if (k < Min(a[i], a[i + 1])) g[a[i + 1] - k][i & 1 ^ 1][1] = Max(g[a[i + 1] - k][i & 1 ^ 1][1], suf + k); suf = Max(suf, g[k][i & 1][0]); } } } for (int s = 0; s < 2; s++) for (int j = 0; j <= a[i]; j++) g[j][i & 1][s] = 0; } ans2 = 0; for (int s = 0; s < 2; s++) for (int i = 0; i <= a ; i++) ans2 = Max(ans2, Max(g[0][n & 1][s], g[i][n & 1][0])); for (int s = 0; s < 2; s++) for (int i = 0; i <= a ; i++) g[i][n & 1][s] = 0; printf("%d %d\n", ans1, ans2); } return 0; }
相关文章推荐
- 【bzoj4720】[NOIP2016]换教室 期望dp
- BZOJ 1138 [POI2009]Baj 最短回文路 DP
- BZOJ 1296 DP
- 【BZOJ1226】学校食堂Dining(状压DP)
- poj 3186 && bzoj 1652: [Usaco2006 Feb]Treats for the Cows(DP)
- [bzoj1072][SCOI2007]排列(状态压缩DP)
- 【BZOJ2435】【Noi2011】道路修建 树形DP
- bzoj 1026 windy数|数位dp
- 【bzoj4726】[POI2017]Sabota? 树形dp
- [bzoj1297][DP][矩阵乘法]迷路
- [bzoj1079][DP]着色方案
- bzoj4521 [Cqoi2016]手机号码 (数位DP)
- BZOJ 1044 HAOI 2008 木棍分割 --二分 DP
- 【BZOJ1419】Red is good(期望DP)
- BZOJ 1492: [NOI2007]货币兑换Cash [CDQ分治 斜率优化DP]
- BZOJ.1597.[Usaco2008 Mar]土地购买(DP 斜率优化)
- 【BZOJ 1096】【ZJOI 2007】仓库建设 DP+斜率优化
- [BZOJ4518][Sdoi2016]征途(斜率优化dp)
- [BZOJ]3209: 花神的数论题 数位DP
- [斜率DP优化]BZOJ 1911: [Apio2010]特别行动队 题解