【练习】DP 做题记录
CF1714D——Color with Occurrences
Description
给定一个字符串 \(t\) 和 \(n\) 个模板串 \(s_i\),若 \(s_i\) 和 \(t\) 的某个子串 \(t[l\ldots r]\) 相等,则可以花费 \(1\) 的代价将这个子串染色,同一段可以被染色多次。求最小代价和方案,或者输出无解。
Solution
由于同一段可以被染色多次,所以最小代价和顺序无关,由于 \(1 \leq \vert t \vert \leq 100\),\(1 \leq \vert s_i \vert \leq 100\) 而且 \(1 \leq n \leq 10\) 可以考虑 \(dp\)。我们可以定义状态 \(dp_{i, j}\) 为将 \(t\) 的前 \(i\) 个字符都染色的最小代价。接下来考虑转移,为了提高效率,我们可以先预处理出 \(len_j\) 表示 \(s_j\) 的长度, \(flag_{i, j}\) 表示 \(s_j\) 能否匹配字符串 \(t\) 中以位置 \(i\) 结尾,长度为 \(len_{j}\) 的子串。这样就可以较轻松地写出状态转移方程,对于每个位置 \(i\),考虑每个能够匹配的\(s_j\),对于每个 \(s_j\),从 \(i - len_j \to i\) 区间内转移。所以状态转移方程如下:
\(dp_{i, j} = \min_{flag_{i, j}=1}\){\(\min_{i - len_j} ^ i dp_k + 1\)}
而记录答案就用 \(pre\) 数组和 \(ans\) 数组记录即可。
Code
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <iostream>
using namespace std;
const int MAXN = 105;
int T;
int n, m;
string s;
int dp[MAXN], pre[MAXN];
int flag[MAXN][MAXN];
int len[MAXN], ans[MAXN];
void print(int k) {
    if (!k)
        return;
    print(pre[k]);
    printf("%d %d\n", ans[k], k - len[ans[k]] + 1);
    return;
}
int main() {
	scanf("%d", &T);
    while (T--) {
        cin >> s;
        n = s.length();
        s = '0' + s;
        memset(dp, 0x3f, sizeof(dp));
        memset(pre, 0, sizeof(pre));
        memset(flag, 0, sizeof(flag));
        scanf("%d", &m);
        for (int i = 1; i <= m; i++) {
            string t;
            cin >> t;
            len[i] = t.length();
            for (int j = len[i]; j <= n; j++)
                if (s.substr(j - len[i] + 1, len[i]) == t)
                    flag[j][i] = 1;
        }
        dp[0] = 0;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                if (!flag[i][j])
                    continue;
                for (int k = i - len[j]; k < i; k++) {
                    if (dp[k] + 1 < dp[i]) {
                        pre[i] = k;
                        ans[i] = j;
                        dp[i] = dp[k] + 1;
                    }
                }
            }
        }
        if (dp[n] == 0x3f3f3f3f)
            printf("-1\n");
        else {
            printf("%d\n", dp[n]);
            print(n);
        }
    }
    return 0;
}
CF1675G——Sorting Pancakes
Description
有 \(n\) 个盘子和 \(m\) 个馅饼,初始时第 \(i\) 个箱子有 \(a_i\) 个馅饼。每次操作可以将一个馅饼移到相邻的箱子里。
求要使得最终数组中任意一个 \(i\) 满足 \(a_i \geq a_{i + 1}\)。
Solution
我们可以定义状态 \(dp_{i, j, k}\) 表示前 \(i\) 盘放 \(j\) 张馅饼,第 \(i\) 盘放 \(k\) 张馅饼,如果我们规定一个盘子只能向右边一个盘子拿或放馅饼。不难发现 \(dp_{i, j, k}\) 可以转移到 \(dp{i + 1, j + p, p}\)。
我们可以定义一个前缀和数组 \(S_i\)。
我们可以开始考虑状态转移方程:因为我们转移时,只和右边一个盘子有关,所以前 \(i\) 个的转移的状态中,前 \(i + 1\) 个盘子上的馅饼数就是 \(S_{i + 1}\)。
所以我们只需从后面一个盘子中去 \(j - p + S_{i + 1}\) 个馅饼即可。
但是我们的时间复杂度为 \(O(n \times m ^ 3)\),这肯定是不行的,考虑优化。
由于 \(f_{i + 1, j + p, p}\) 会被搜有的 \(f_{i, j, k}\) 转移,所以,考虑倒着枚举 \(k\)。
设 \(minv = \min_{u = k} ^ m f_{i, j, u}\),此时的状态转移方程就简化为:
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 255, INF = 1e9;
int n, m, ans;
int a[MAXN], sum[MAXN];
int dp[MAXN][MAXN][MAXN];
int main() {
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= n; i++)
		scanf("%d", &a[i]), sum[i] = sum[i - 1] + a[i];
	memset(dp, 0x3f, sizeof(dp));
	dp[0][0][m] = 0;
	for (int i = 0; i < n; i++)
		for (int j = 0; j <= m; j++) {
			int minv = INF;
			for (int k = m; k >= 0; k--) {
				minv = min(minv, dp[i][j][k]);
				if (j + k <= m)
					dp[i + 1][j + k][k] = min(dp[i + 1][j + k][k], minv + abs(j + k - sum[i + 1]));
			}
		}
	ans = INF;
	for (int i = 0; i <= m; i++)
		ans = min(ans, dp[n][m][i]);
	printf("%d", ans);
	return 0;
}
CF1625C——Road Optimization
Description
Solution
Code
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int MAXN = 505, INF = 1e9;
int n, l, k;
int d[MAXN], a[MAXN];
int dp[MAXN][MAXN];
int main() {
	scanf("%d %d %d", &n, &l, &k);
	for (int i = 1; i <= n; i++)
		scanf("%d", &d[i]);
	d[n + 1] = l;
	for (int i = 1; i <= n; i++)
		scanf("%d", &a[i]);
	memset(dp, 0x3f, sizeof(dp));
    for (int i = 0; i <= k; i++)
		dp[1][i] = 0;
	for (int i = 2; i <= n + 1; i++)
		for (int j = 0; j <= min(i - 1, k); j++)
			for (int u = 0; u <= j; u++)
				dp[i][j] = min(dp[i][j], dp[i - u - 1][j - u] + (d[i] - d[i - u - 1]) * a[i - u - 1]);
	int ans = INF;
	for (int i = 0; i <= k; i++)
		ans = min(ans, dp[n + 1][i]);
	printf("%d", ans);
	return 0;
}
本文来自博客园,作者:zhou_ziyi,转载请注明原文链接:https://www.cnblogs.com/zhouziyi/p/16588224.html

 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号