【练习】DP 做题记录

CF1714D——Color with Occurrences

Description

给定一个字符串 tn 个模板串 si,若 sit 的某个子串 t[lr] 相等,则可以花费 1 的代价将这个子串染色,同一段可以被染色多次。求最小代价和方案,或者输出无解。

Solution

由于同一段可以被染色多次,所以最小代价和顺序无关,由于 1|t|1001|si|100 而且 1n10 可以考虑 dp。我们可以定义状态 dpi,j 为将 t 的前 i 个字符都染色的最小代价。接下来考虑转移,为了提高效率,我们可以先预处理出 lenj 表示 sj 的长度, flagi,j 表示 sj 能否匹配字符串 t 中以位置 i 结尾,长度为 lenj 的子串。这样就可以较轻松地写出状态转移方程,对于每个位置 i,考虑每个能够匹配的sj,对于每个 sj,从 ilenji 区间内转移。所以状态转移方程如下:
dpi,j=minflagi,j=1{minilenjidpk+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 个箱子有 ai 个馅饼。每次操作可以将一个馅饼移到相邻的箱子里。
求要使得最终数组中任意一个 i 满足 aiai+1

Solution

我们可以定义状态 dpi,j,k 表示前 i 盘放 j 张馅饼,第 i 盘放 k 张馅饼,如果我们规定一个盘子只能向右边一个盘子拿或放馅饼。不难发现 dpi,j,k 可以转移到 dpi+1,j+p,p

我们可以定义一个前缀和数组 Si

我们可以开始考虑状态转移方程:因为我们转移时,只和右边一个盘子有关,所以前 i 个的转移的状态中,前 i+1 个盘子上的馅饼数就是 Si+1

所以我们只需从后面一个盘子中去 jp+Si+1 个馅饼即可。

fi+1,j+p,p=min{fi,j,k+|j+pSi+1|}

但是我们的时间复杂度为 O(n×m3),这肯定是不行的,考虑优化。

由于 fi+1,j+p,p 会被搜有的 fi,j,k 转移,所以,考虑倒着枚举 k

minv=minu=kmfi,j,u,此时的状态转移方程就简化为:

fi+1,j+p,p=min{minv+|j+pSi+1|}

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;
}
posted @   zhou_ziyi  阅读(51)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示