1. 程式人生 > 其它 >題解 P5851 [USACO19DEC]Greedy Pie Eaters P

題解 P5851 [USACO19DEC]Greedy Pie Eaters P

題解 P5851 [USACO19DEC]Greedy Pie Eaters P

應該比較好看出是區間 DP(

n 個派, m 個奶牛
f[l][r][i] 表示第 i 個派尚未被吃時能吃掉它和能吃掉派區間 [l,r] 的奶牛(們)的最大體重
d[l][r] 表示派 [l,r] 全被吃掉後最大的奶牛總體重

對於每一個 f[l][r][i], 派 i 只能是位於區間最左/右邊的(因為要吃就必須把 [l,r] 全吃掉), 於是可得出:
\(f[l][r][i] = max(f[l+1][r][i], f[l][r+1][i]), l <= i <= r\)

分類討論每一個 d[l][r] 的取值情況

  1. d[l][r] 可以由兩個子區間合併而成
    \(d[l][r] = d[l][k] + d[k+1][r], l<=k<r\)
  2. d[l][r] 可以由一塊沒有被吃過的派和剩餘區間組成
    \(d[l][r] = f[l][r][k] + d[l][k - 1] + d[k + 1][l],l<=k<=r\)

程式碼如下

#include <stdio.h>
#include <algorithm>

int n, m;
int f[303][303][303];
int d[303][303];

int main() {
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= m; i++) { 
		int x, y, z;
		scanf("%d %d %d", &x, &y, &z); 
		for (int j = y; j <= z; j++)
			f[y][z][j] = x;
	}
	for (int len = 1; len <= n; len++)
		for (int l = 1; l + len - 1 <= n; l++) {
			int r = l + len - 1;
			for (int k = l; k <= r; k++)
				f[l][r][k] = std:: max(f[l][r][k], f[l][r - 1][k]),
				f[l][r][k] = std:: max(f[l][r][k], f[l + 1][r][k]);
		}
	for (int len = 1; len <= n; len++)
		for (int l = 1; l + len - 1 <= n; l++) {
			int r = l + len - 1;
			// 1. 兩個子區間合併而成 
			for (int k = l; k < r; k++)
				d[l][r] = std:: max(d[l][r], d[l][k] + d[k + 1][r]);
			// 2. 一個未吃過的派 k 與剩餘部分組成 
			for (int k = l; k <= r; k++) 
				d[l][r] = std:: max(d[l][r], d[l][k - 1] + f[l][r][k] + d[k + 1][r]);
		}
	printf("%d\n", d[1][n]);
	return 0;
}
( ゚∀゚)o彡゜ ヒーコー ヒーコー!