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;
}
posted @ 2022-05-07 22:56  浅渊  阅读(31)  评论(0)    收藏  举报