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

不容易直接做,考虑差分转化为求深度不超过 $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 }
View Code

第一个数必然正,第二个数必然负,后面的数正负都有可能,构造方案的时候可以强制让正恰好被减两次,负负得正。

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

棋盘大小 $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 }
View Code
posted @ 2022-02-27 22:45  jhqqwq  阅读(26)  评论(0编辑  收藏  举报