zoukankan      html  css  js  c++  java
  • [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 ight])中的派(包括两端),并且没有两头奶牛喜欢吃相同范围的派。第(i)头奶牛有一个体重(w_i),这是一个在(left[ 1,10^6 ight])中的正整数。

    (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
    ", dp[1][n]);
    	return 0;
    }
    

    总结回顾

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

    参考文献

  • 相关阅读:
    49个128*128像素的天气png图标
    非常漂亮的千千静听皮肤[Warp]出自梦梦的作品
    腾讯的面试题
    什么是函数参数,如何传递函数参数
    我看了这视频,情不自禁的笑了
    WindowBlinds基础使用教程
    晒工资网站
    一个可以搜索word文档,电子表格,PDF,幻灯片的搜索引擎
    网页设计常用png小图标一套
    使用U盘时候的”无法停用通用卷”的解决办法
  • 原文地址:https://www.cnblogs.com/zach20040914/p/13516654.html
Copyright © 2011-2022 走看看