[USACO19DEC]Greedy Pie Eaters

题目:Greedy Pie Eaters

网址:https://www.luogu.com.cn/problem/P5851

题目描述

\(Farmer John\)\(M\)头奶牛,为了方便,编号为\(1,\dots,M\)。这些奶牛平时都吃青草,但是喜欢偶尔换换口味。\(Farmer John\)一天烤了\(N\)个派请奶牛吃,这\(N\)个派编号为\(1,\dots,N\)。第\(i\)头奶牛喜欢吃编号在\(\left[ l_i,r_i \right]\)中的派(包括两端),并且没有两头奶牛喜欢吃相同范围的派。第\(i\)头奶牛有一个体重\(w_i\),这是一个在\(\left[ 1,10^6 \right]\)中的正整数。

\(Farmer John\)可以选择一个奶牛序列 \(c_1,c_2,\dots,c_K\),并让这些奶牛按这个顺序轮流吃派。不幸的是,这些奶牛不知道分享!当奶牛 吃派时,她会把她喜欢吃的派都吃掉——也就是说,她会吃掉编号在 \([l_{c_i},r_{c_i}]\)中所有剩余的派。\(Farmer John\) 想要避免当轮到一头奶牛吃派时,她所有喜欢的派在之前都被吃掉了这样尴尬的情况。因此,他想让你计算,要使奶牛按 \(c_1,c_2,\dots,c_K\)的顺序吃派,轮到这头奶牛时她喜欢的派至少剩余一个的情况下,这些奶牛的最大可能体重(\(w_{c_1}+w_{c_2}+\ldots+w_{c_K}\))是多少。

输入格式

第一行包含两个正整数\(N,M\)

接下来\(M\)行,每行三个正整数 \(w_i,l_i,r_i\)

输出格式

输出对于一个合法的序列,最大可能的体重值。

输入输出样例

输入 #1

2 2
100 1 2
100 1 1

输出 #1

200

说明/提示

样例解释

在这个样例中,如果奶牛\(1\)先吃,那么奶牛\(2\)就吃不到派了。然而,先让奶牛\(2\)吃,然后奶牛\(1\)只吃编号为\(2\)的派,仍可以满足条件。

对于全部数据,\(1 \le N \le 300,1 \le M \le \dfrac{N(N-1)}{2},1 \le l_i,r_i \le N,1 \le w_i \le 10^6\)

数据范围

对于测试点\(2-5\),满足\(N \le 50,M \le 20\)

对于测试点\(6−9\),满足\(N \le 50\)

USACO 2019 December 铂金组T1


这是USACO质量极其好得一道。
我们急需解决以下两个问题:

  • 选取问题,选哪几个使得权重最大又合法;
  • 顺序问题,顺序决定是否满足题意。
    对这两个问题的理解是否深入将直接导致这道题的做法是否简洁。

首先,我们考虑:若最终的答案中所选取的奶牛构成一个集合\(S\),则\(S\)中一定有一中排列方式使得每头牛都能分得“一杯羹“。
这是毋庸置疑的一点。
更近的,如果我们只要找到一种方法判定所选择的奶牛是否全都有派,那么我们仅需按照这个方法处理该问题。
事实上,注意到,不妨考虑一种情况:若该奶牛所属区间为\([l, r]\),则若该区间中存在一个位置k没有被覆盖(选择),那么这头牛就可以被接受。
也就是说,不管顺序如何,只要保证至少有一个位置没有被覆盖,那么该奶牛可以被选择的。

我们把顺序问题解决了。

在以上的基础上,我们考虑使用DP;
\(dp[l,r]\)表示区间\([l,r]\)的派最多能获得多大的权重和。
显然有\(dp[l,r]=max(dp[l,k]+dp[k+1,r])\),这表示一个区间是由子区间拼凑出来的。
等等,有些情况我们并没有包括其中,比如:

注意到,这种情况是因为两个子区间并没有“无缝衔接”,中间似乎贴了一段“区间”(这也符合我们刚刚讨论过的顺序问题),因此,DP方程还应有:\(dp[l,r]=max(dp[l,k-1]+f[l,r,k]+dp[k+1,r])\);
为什么只预留了一个长度?为什么对于中间的那一个区间就不能单独覆盖更多的位置。这又是一个贪心。前者更优。

另有,\(f\)指的是区间\([l,r]\)中包含第\(k\)个位置的所有奶牛权重最大的一只奶牛的权重。预处理即可,则必有\(f[i,j,k]=max(f[i+1,j,k],f[i,j-1,k],w[i,j])\)。边界问题。

C ++ AC代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
const int SIZE = 50000 + 10;
struct COW
{
	int l, r, w;
} c[SIZE];
int n, m, pos[310][310];
int p[310][310][310], dp[310][310];//dp[i][j] 指的是区间 [i, j] 被吃完的最大体重值
//有方程:  dp[i][j] = max(dp[i][k] + d[k][j], d[i][k - 1] + p[i][j][k], dp[k + 1][j]);
int main()
{
	scanf("%d %d", &n, &m);
	memset(pos, 0, sizeof(pos));
	for(int i = 0; i < m; ++ i)
	{
		scanf("%d %d %d", &c[i].w, &c[i].l, &c[i].r);
		pos[c[i].l][c[i].r] = c[i].w;
	}
	memset(dp, 0, sizeof(dp));
	memset(p, 0, sizeof(p));
	for(int len = 1; len <= n; ++ len)
	{
		for(int i = 1, j = i + len - 1; j <= n; ++ i, ++ j)
		{
			for(int k = i; k <= j; ++ k)
			{
				p[i][j][k] = pos[i][j];
				p[i][j][k] = max(p[i][j][k], max((i < k) * p[i + 1][j][k], (k < j) * p[i][j - 1][k]));
			}
		}
	}
	for(int len = 1; len <= n; ++ len)
	{
		for(int i = 1, j = i + len - 1; j <= n; ++ i, ++ j)
		{
			int &ans = dp[i][j];
			for(int k = i; k <= j; ++ k)
			{
				ans = max(ans, dp[i][k] + dp[k + 1][j]);
				ans = max(ans, dp[i][k - 1] + p[i][j][k] + dp[k + 1][j]);
			}
		}
	}
	printf("%d\n", dp[1][n]);
	return 0;
}

总结回顾

  1. 这道题从看起来好像需要排序满足题意,到用DP解决完成,发生了题目的转化。事实上,很多看起来费劲的题目,往往要发觉特性,将其化险为夷。
  2. 另外这道题的两个转移值得思索回味。

参考文献

posted @ 2020-08-17 11:35  大秦帝国  阅读(208)  评论(0编辑  收藏  举报