备战Noip2018模拟赛5(B组)T2 Tree 采果子

10月4日备战Noip2018模拟赛5(B组)

T2 Tree采果子

题目描述

 LYL大牛今天心情不错,于是走到埃及郊外旅游。他边走边向四周望望,发现周围有许多果树。这些树之间互相到达的时间LYL是知道的(假定每两棵树之间都拥有独立的边可以到达)。他数出了这些果树上果子的个数,并且估了估每个的价格(真是个奇怪的人).Lyl规定了一种采摘方案,就是对于第我棵树来说,它有一个[I]个果子,且所有果子价值为S [I]中,摘取时间为C [1](小时)。并且,当他摘了第我个树上的果子后,后面他所选择去摘的果树上的果子数必须大于第我棵树上的果子数目,说白了就是一个上升序列;并且每到一棵树,他都必须摘下该树上的所有果子一开始,LYL可以在任意一棵树,他只有米小时,那么,在他所拥有的限定时间内,他想知道,这样摘取的最大价值是多少?

输入格式

文件输入侧tree.in第一行2个数为:n(表示这条路上的大树数),M(总共时间)

    接下来第n + 1行,每行三个数a [i],s [i],c [i](第i + 1行输入的为第i颗果树的信息)

    且保证有1 <= A [1] <= 2 ^ 31-1; 1 <= S [1] + S [2] + ... + S [N] <= 2 ^ 31-1; S [I]> = 0; 1 <= C [1] <= 100

    接下来的Ñ行,每行Ñ个数,第I行第Ĵ个数表示从树我到树Ĵ的时间(0 <= T [I,J] <= 100)。

输出格式

输出文件tree.out有且仅有一个数,即按这样方法摘取的最大价值。

输入样例

4 10
1 10 2
2 5 3
3 6 1
4 9 4
0 1 2 3
1 0 3 4
2 3 0 5
3 4 5 0

输出样例

21

样例解释

先摘第1棵树上的,再摘第2棵树上的,然后第3棵树上的

数据范围

 对于60%的数据,1 <= N <= 60,1 <= m <= 100;

对于100%的数据,1 <= N <= 100,1 <= m <= 1000。

其实考模拟赛的时候,题面改了一下,因为太皮了,所以贴了原题

看到了美味程度(价值),采摘时间(重量),很容易看出这道题其实就是01背包,但是与传统的01背包不同的是,

  1. 因为两棵苹果树之前有距离,需要耗费时间来走,那么多加一层循环来找前面采摘的苹果就可以了,f [i] [j]表示在ih,第i棵苹果树所采摘苹果的最大美味程度
  2. 因为每次采摘的苹果要比上一次多,所以可以先排序一遍,但是要注意此时的苹果树的编号改变了,那么和邻接矩阵的对应也改变了,所以在结构中加一个没来储存苹果树原来的编号

代码

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 101;
const int M = 1005;

struct Node{
	int apple;
	int delicious;
	int pick;
	int no;             //记录苹果树的编号
}a[N];

int n, m, i, j, r, app, del, pic, Max;
int f[M][N], adj[N][N];

bool cmp (Node a, Node b)
{
	return a.apple < b.apple;
}

int main ()
{
	freopen ("tree.in", "r", stdin);
	freopen ("tree.out", "w", stdout);
	
	scanf ("%d %d", & n, & m);
	for (i = 1; i <= n; i ++){
		scanf ("%d %d %d", & app, & del, & pic);
		a[i].apple = app;
		a[i].delicious = del;
		a[i].pick = pic;
		a[i].no = i;
	}
	for (i = 1; i <= n; i ++){
		for (j = 1; j <= n; j ++){
			scanf ("%d", & adj[i][j]);            
		}
	}
	
	sort (a + 1, a + 1 + n, cmp);            //按苹果数排序
	
	for (i = 1; i <= n; i  ++){
		f[a[i].pick][i] = a[i].delicious;            //每棵树耗费自己的采摘时间可以得到的美味值
	}
	
	for (i = 1; i <= n; i ++){
		for (j = 1; j <= m; j ++){
			f[j][i] = max (f[j][i], f[j - 1][i]);
			for (r = 1; r < i; r ++){            //和传统01背包不同的地方,找上一棵树
				if (j - adj[a[r].no][a[i].no] - a[i].pick >= 0 
					&& a[i].apple != a[r].apple){
						f[j][i] = max (f[j][i], f[j - adj[a[r].no][a[i].no] - a[i].pick][r] + a[i].delicious);
					}
			}
		}
	}
	
	Max = 0;
	for (i = 1; i <= n; i ++){
		Max = max (Max, f[m][i]);            //不确定到第几棵树的最大,从第一颗树开始找一遍
	}
	
	printf ("%d", Max);	
	
	fclose (stdin);
	fclose (stdout);
	return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值