Educational Codeforces Round 39 (Rated for Div. 2) -- D. Timetable (DP)

写得很折磨人,每次dp都写个一个多小时,写出来明明觉得不难 .

  1. 题目大意:可以进行K次操作,把删除1,进行k次操作后每行第一个 1 和最后一个 1 的位置 相减的绝对值加 1 得到的结果最小。

  2. 做法:每次肯定是要从左删或者从右边删,然后顺着这个思路,先把每行的进行小于等于k次操作时, 每行最小的值存下来, 我用f[i][j], i 代表第几行, j 代表几次操作。这个直接暴力,枚举所有情况。左零次,右边 1 ...... k 次这样暴力。把值求出来后,就直接套分组背包的模板就行。

const int N = 510;

char g[N][N];
int  f[N][N], dp[N][N], s[N];

void solve()
{
    int n, m, k;
    cin >> n >> m >> k;
    int sum = 0;
    memset(f, -0x3f, sizeof f);
    for (int i = 1; i <= n; i ++)
    {
    	int a[m + 1] = {0};
    	int l = 1, r = 0;
		for (int j = 1; j <= m; j ++)
		{
			cin >> g[i][j];
			if (g[i][j] == '1') a[++ r] = j;
		}
		f[i][0] = 0;
		if (r == 0) continue;
		sum += a[r] - a[l] + 1;
		int maxx = min(k, r - l + 1);
		for (int j = 1; j <= maxx; j ++)
		{
			if (j == r - l + 1) {f[i][j] = a[r] - a[l] + 1;continue;}
			for (int p = 0; p <= j; p ++)
			{
				int res = a[r - (j - p)] - a[l + p] + 1;
				f[i][j] = max(f[i][j], a[r] - a[l] + 1 - res);
			}
		}
		s[i] = maxx;
    }
    
    for (int i = 1; i <= n; i ++)
    {
    	for (int j = 0; j <= k; j ++)
    	{
    		dp[i][j] = dp[i - 1][j];
    		for (int l = 0; l <= s[i]; l ++)
    		{
    			if (j >= l) dp[i][j] = max(dp[i][j], dp[i - 1][j - l] + f[i][l]);
    		}
    	}
    }
    cout << sum - dp[n][k] << '\n';
}
posted @ 2023-04-23 21:45  N再再  阅读(21)  评论(0)    收藏  举报