CF946D Timetable
题目链接
题目的大意是Ivan他一周的课表是已知的,用一串\(01\)串来表示一天的上课情况,\(1\)表示的这个时间段是有课的\(0\)表示的是没有课的。现在Ivan这一周可以翘\(k\)节课,求出来Ivan在学校的最短时间应该是多长。
因为每一天只有翘掉首尾的课是可以减少这一天对答案的贡献的,所以翘课的时候应该选择对首尾的\(1\)进行处理,这样才能够减少在学校的时间。在处理每一天的时候,我们应该去维护\(mn[i][j]\)表示在第\(i\)天翘\(j\)节课的最小在校时间是多久。处理完每一天的之后,\(dp[i][j]\)用\(dp[i - 1][j - len] + mn[i][len]\)来更新。
#include <bits/stdc++.h>
using i64 = long long;
int n, m, k;
i64 dp[510][510];
int p[510], mn[510][510], a[510];
char s[510];
int main() {
scanf("%d%d%d", &n, &m, &k);
memset(mn, 0x3f, sizeof mn);
for (int i = 1; i <= n; i ++ ) {
scanf("%s", s + 1);
int len = 0;
for (int j = 1; j <= m; j ++ ) if (s[j] & 1)
a[++ len] = j;
mn[i][len] = 0;
p[i] = len;
for (int j = 1; j <= len; j ++ ) {
for (int t = j; t <= len; t ++ ) {
mn[i][len - (t - j + 1)] = std::min(mn[i][len - (t - j + 1)], a[t] - a[j] + 1);
}
}
}
memset(dp, 0x3f, sizeof dp);
for (int i = 0; i <= k; i ++ ) dp[0][i] = 0;
for (int i = 1; i <= n; i ++ ) {
for (int j = 0; j <= k; j ++ ) {
for (int t = 0; t <= std::min(p[i], j); t ++ ) {
dp[i][j] = std::min(dp[i][j], dp[i - 1][j - t] + mn[i][t]);
}
}
}
printf("%lld\n", dp[n][k]);
return 0 ^ 0;
}