Timetable
题意:
给出 \(n\) 行 \(m\) 列 \(01\) 串,对于每一行所要花费的代价是行中第一个 \(1\) 和最后一个 \(1\) 之间的距离加一,现在你有魔法可以去除掉 \(k\) 个 \(1\),问去掉不多于 \(k\) 个 \(1\) 的情况下,你所能获得的最小代价是多少。
思路:
看到有 \(k\) 次限制求最小代价,不难想到需要用到 \(DP\) 来解决,不妨考虑设状态 \(dp[i][j]\) 表示在前 \(i\) 个串中逃了 \(j\) 次课最少的上课时间是多少。那么就可以将逃了多少次课视作一个物品的容量,每一天上课的时间看作是物品的价值,这样就转化成了一个背包问题。但是每一天到底哪些课要逃,哪些课不逃不太好处理,所以需要对每一天做一个预处理。
定义一个数组 \(v[i][j]\) 表示第 \(i\) 天翘了 \(j\) 节课的最小代价,因为题目说了,一天的在校时间是最后一节课的时间减去第一节课的时间,所以只有逃首尾两端的课才可以让贡献变小,那么只需要去 \(O\left(n ^ 2\right)\) 的循环计算一下中间要上哪些课就可以了
memset(v, 0x3f, sizeof v);
for (int i = 1; i <= n; i++) {
string s;
cin >> s;
int len = 0;
for (int j = 0; j < m; j++) {
if (s[j] & 1) a[++len] = j + 1; //记录哪些时间段有课
}
v[i][len] = 0;
siz[i] = len;
for (int j = 1; j <= len; j++)
for (int k = j; k <= len; k++)
v[i][len - (k - j + 1)] = min(v[i][len - (k - j + 1)], a[k] - a[j] + 1); //中间要上课的时间
}
之后就是一个基础的背包转移
memset(dp, 0x3f, sizeof dp);
for (int i = 0; i <= p; i++) dp[0][i] = 0;
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= p; j++) {
for (int k = 0; k <= min(j, siz[i]); k++) {
dp[i][j] = min(dp[i][j], dp[i - 1][j - k] + v[i][k]);
}
}
}
printf("%d\n", dp[n][p]);
标签:哪些,CF946D,len,int,Timetable,动态,代价,dp
From: https://www.cnblogs.com/Haven-/p/17058896.html