DP 做题笔记(1)
需要记录每个前缀的每个字符的最后一次出现位置来优化 DFS 输出方案。
1 const int N = 85, S = 26; 2 string s, t; 3 int ans, a[N][S], b[N][S], dp[N][N]; 4 vector<string> res; 5 6 void dfs(int n, int m, string tmp) 7 { 8 if (dp[n][m] + tmp.size() < ans) return; 9 if (tmp.size() == ans) { res.push_back(tmp); return; } 10 if (dp[n][m] == dp[n - 1][m - 1] + 1 && s[n] == t[m]) 11 tmp.push_back(s[n]), dfs(n - 1, m - 1, tmp); 12 else for (int ch = 0; ch < 26; ++ch) dfs(a[n][ch], b[m][ch], tmp); 13 } 14 15 int main() 16 { 17 ios::sync_with_stdio(false); 18 cin >> s >> t; int n = s.size(), m = t.size(); 19 s = '#' + s, t = '#' + t; 20 for (int i = 1; i <= n; ++i) 21 { 22 for (int j = 0; j < 26; ++j) a[i][j] = a[i - 1][j]; 23 a[i][s[i] - 'a'] = i; 24 } 25 for (int i = 1; i <= m; ++i) 26 { 27 for (int j = 0; j < 26; ++j) b[i][j] = b[i - 1][j]; 28 b[i][t[i] - 'a'] = i; 29 } 30 for (int i = 1; i <= n; ++i) 31 for (int j = 1; j <= m; ++j) 32 if (s[i] == t[j]) dp[i][j] = dp[i - 1][j - 1] + 1; 33 else dp[i][j] = Max(dp[i - 1][j], dp[i][j - 1]); 34 ans = dp[n][m]; dfs(n, m, ""); 35 for (int i = 0; i < res.size(); ++i) reverse(res[i].begin(), res[i].end()); 36 sort(res.begin(), res.end()); 37 for (int i = 0; i < res.size(); ++i) cout << res[i] << '\n'; 38 fwrite(pbuf, 1, pp - pbuf, stdout); return 0; 39 }
不容易直接做,考虑差分转化为求深度不超过 $D$,枚举代表元暴力 DP 即可,时间复杂度 $O(n^7)$。
1 const int p = 11380; 2 int N, M, K, D, dp[35][15][15][15]; 3 4 int main() 5 { 6 read(N, M, K, D); dp[0][0][0][0] = 1; 7 if (!N && !M && !K && !D) { putchar('1'); return 0; } 8 else if (!D) { putchar('0'); return 0; } 9 for (int d = 1; d <= D; ++d) 10 for (int n = 0; n <= N; ++n) 11 for (int m = 0; m <= M; ++m) 12 for (int k = 0; k <= K; ++k) 13 { 14 if (!n && !m && !k) { dp[d][n][m][k] = 1; continue; } 15 int tmp = 0; 16 for (int i = 1; i <= n; ++i) 17 for (int j = 0; j <= m; ++j) 18 for (int l = 0; l <= k; ++l) 19 (tmp += dp[d - 1][i - 1][j][l] * dp[d][n - i][m - j][k - l]) %= p; 20 for (int j = 1; j <= m; ++j) 21 for (int l = 0; l <= k; ++l) 22 (tmp += dp[d - 1][0][j - 1][l] * dp[d][n][m - j][k - l]) %= p; 23 for (int l = 1; l <= k; ++l) 24 (tmp += dp[d - 1][0][0][l - 1] * dp[d][n][m][k - l]) %= p; 25 dp[d][n][m][k] = tmp; 26 } 27 print((dp[D][N][M][K] - dp[D - 1][N][M][K] + p) % p); 28 fwrite(pbuf, 1, pp - pbuf, stdout); return 0; 29 }
第一个数必然正,第二个数必然负,后面的数正负都有可能,构造方案的时候可以强制让正恰好被减两次,负负得正。
1 const int V = 1e4; 2 int n, t, a[105], opt[105], dp[105][20005]; 3 4 int main() 5 { 6 read(n, t); t += V; 7 for (int i = 1; i <= n; ++i) read(a[i]); 8 dp[1][V + a[1]] = 1, dp[2][V + a[1] - a[2]] = -1; 9 for (int i = 3; i <= n; ++i) 10 { 11 for (int j = V << 1; j >= a[i]; --j) 12 if (dp[i - 1][j - a[i]]) dp[i][j] = 1; 13 for (int j = (V << 1) - a[i]; ~j; --j) 14 if (dp[i - 1][j + a[i]]) dp[i][j] = -1; 15 } 16 for (int i = n; ~i; --i) opt[i] = dp[i][t], ~dp[i][t] ? t -= a[i] : t += a[i]; 17 for (int i = 3; i <= n; ++i) ~opt[i] ? pc('2') : pc('1'), pc('\n'); pc('1'); 18 fwrite(pbuf, 1, pp - pbuf, stdout); return 0; 19 }
棋盘大小 $n=8$,直接拆公式然后暴力记忆化搜索即可,时间复杂度 $O(n^5)$。
1 const int N = 10, INF = 1e9; 2 int n = 8, k, a[N][N], sum[N][N], dp[N][N][N][N][N << 1]; 3 4 inline int get_sum2(int _x1, int _y1, int _x2, int _y2) 5 { 6 int tmp = sum[_x2][_y2] - sum[_x1 - 1][_y2] - sum[_x2][_y1 - 1] + sum[_x1 - 1][_y1 - 1]; 7 return tmp * tmp; 8 } 9 int dfs(int _x1, int _y1, int _x2, int _y2, int k) 10 { 11 if (~dp[_x1][_y1][_x2][_y2][k]) return dp[_x1][_y1][_x2][_y2][k]; 12 if ((_x2 - _x1 + 1) * (_y2 - _y1 + 1) < k) return dp[_x1][_y1][_x2][_y2][k] = INF; 13 if (k == 1) return dp[_x1][_y1][_x2][_y2][k] = get_sum2(_x1, _y1, _x2, _y2); 14 int tmp = INF; 15 for (int i = _x1; i < _x2; ++i) 16 { 17 tmp = Min(tmp, get_sum2(_x1, _y1, i, _y2) + dfs(i + 1, _y1, _x2, _y2, k - 1)); 18 tmp = Min(tmp, get_sum2(i + 1, _y1, _x2, _y2) + dfs(_x1, _y1, i, _y2, k - 1)); 19 } 20 for (int i = _y1; i < _y2; ++i) 21 { 22 tmp = Min(tmp, get_sum2(_x1, _y1, _x2, i) + dfs(_x1, i + 1, _x2, _y2, k - 1)); 23 tmp = Min(tmp, get_sum2(_x1, i + 1, _x2, _y2) + dfs(_x1, _y1, _x2, i, k - 1)); 24 } 25 return dp[_x1][_y1][_x2][_y2][k] = tmp; 26 } 27 28 int main() 29 { 30 read(k); memset(dp, -1, sizeof(dp)); 31 for (int i = 1; i <= n; ++i) 32 for (int j = 1; j <= n; ++j) 33 { 34 read(a[i][j]); 35 sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + a[i][j]; 36 } 37 printf("%.3lf", sqrt((dfs(1, 1, n, n, k) - 1.0 * sum[n][n] * sum[n][n] / k) / k)); 38 fwrite(pbuf, 1, pp - pbuf, stdout); return 0; 39 }
- P4072 [SDOI2016]征途
- 322. 消木块(UVA10559 方块消除 Blocks,P2135 方块消除,CF1132F Clear the String,CF1107E Vasya and Binary String,SP6340 ZUMA - ZUMA)
- 319. 折叠序列(P2400 秘密文件)
- 326. XOR和路径(P3211 [HNOI2011]XOR和路径)
- 330. 估算(SP16809 EST - Estimation)
- 329. 围栏障碍训练场
- 332. 股票交易(P2569 [SCOI2010]股票交易)
- 331. 干草堆(P4954 [USACO09OPEN]Tower of Hay G)
- P5665 [CSP-S2019] 划分