NOI 4.5 动态规划 2454:雷涛的小猫

题目来源:http://noi.openjudge.cn/ch0405/2454/

2454:雷涛的小猫

总时间限制20000ms     单个测试点时间限制10000ms     内存限制65536kB

描述

雷涛同学非常的有爱心,在他的宿舍里,养着一只因为受伤被救助的小猫(当然,这样的行为是违反学生宿舍管理条例的)。在他的照顾下,小猫很快恢复了健康,并且愈发的活泼可爱了。

可是有一天,雷涛下课回到寝室,却发现小猫不见了!经过一番寻找,才发现她正趴在阳台上对窗外的柿子树发呆

在北京大学的校园里,有许多柿子树,在雷涛所在的宿舍楼前,就有N棵。并且这N棵柿子树每棵的高度都是H。冬天的寒冷渐渐笼罩了大地,树上的叶子渐渐掉光了,只剩下一个个黄澄澄的柿子,看着非常喜人。而雷涛的小猫恰好非常的爱吃柿子,看着窗外树上的柿子,她十分眼馋,于是决定利用自己敏捷的跳跃能力跳到树上去吃柿子。

小猫可以从宿舍的阳台上跳到窗外任意一棵柿子树的树顶。之后,她每次都可以在当前位置沿着当前所在的柿子树向下跳1单位距离。当然,小猫的能力远不止如此,她还可以在树之间跳跃。每次她都可以从当前这棵树跳到另外的任意一棵,在这个过程中,她的高度会下降Delta单位距离。每个时刻,只要她所在的位置有柿子,她就可以吃掉。整个吃柿子行动一直到小猫落到地面上为止。

雷涛调查了所有柿子树上柿子的生长情况。他很想知道,小猫从阳台出发,最多能吃到多少柿子?他知道写一个程序可以很容易的解决这个问题,但是他现在懒于写任何代码。于是,现在你的任务就是帮助雷涛写一个这样的程序。

图为N =3, H = 10, Delta = 2的一个例子。小猫按照图示路线进行跳跃,可以吃到最多的8个柿子


输入

输入文件的第一行有三个以空格分隔的整数,分别代表NHDelta

接下来的N行,每行第一个整数为Ni,代表第i棵树上的柿子数量。

接下来是Ni个整数,每个整数Tij代表第i棵柿子树的Tij高度上长有一个柿子。

输出

输出仅包含一个整数,即小猫最多吃到的柿子数。

样例输入

3 10 2
3 1 4 10
6 3 5 9 7 8 9
5 4 5 3 6 9

样例输出

8

提示

1 ≤ NH ≤ 2000
0 ≤ Ni ≤ 5000
1 ≤ Delta ≤ N,1 ≤ Tij ≤ H
输入文件大小不大于40960KB

来源

Excalibur, 2008

 -----------------------------------------------------

思路

动态规划

dp[i][j]: 从宿舍跳到第i棵树的第j层一共吃到的柿子数

    dp[i][j] =max(dp[i][j+1], hmax[j+d])

    hmax[h] = max(dp[i][h])

其中hmax的引入巧妙地将O(n^3)降至O(n^2).

-----------------------------------------------------

代码

#include
#include
#include
using namespace std;

long long dp[2005][2005] = {};						// 从宿舍跳到第i棵树的第j层一共吃到的柿子数
int mat[2005][2005] = {};							// 每棵树每层的柿子个数
long long hmax[2005] = {};							// max(dp[i][h]), i=1,2,...,n

// 动态规划 dp[i][j] = max(dp[i][j+1], hmax[j+d])
//			hmax[h] = max(dp[i][h])

int main()
{
#ifndef ONLINE_JUDGE
	ifstream fin ("0405_2454.txt");
	int n,h,d,i,j,hn,tmph;
	long long mymax = 0;
	fin >> n >> h >> d;
	for (i=1; i<=n; i++)
	{
		fin >> hn;
		for (j=1; j<=hn; j++)
		{
			fin >> tmph;
			mat[i][tmph]++;
		}
	}
	fin.close();
	mymax = 0;
	for (i=1; i<=n; i++)								// 初始化树顶
	{
		dp[i][h] = mat[i][h];
		mymax = max(mymax, dp[i][h]);
	}
	hmax[h] = mymax;
	for (j=h-1; j>=0; j--)								// dp[i][j] = max(dp[i][j+1], hmax[j+d]);
	{
		mymax = 0;
		for (i=1; i<=n; i++)
		{
			if (j+d<=h)
			{
				dp[i][j] = mat[i][j] + max(dp[i][j+1], hmax[j+d]);
			}
			else
			{
				dp[i][j] = mat[i][j] + dp[i][j+1];
			}
			mymax = max(mymax, dp[i][j]);
		}
		hmax[j] = mymax;
	}
	mymax = 0;
	for (i=1; i<=n; i++)
	{
		mymax = max(mymax, dp[i][0]);
	}
	cout << mymax;
	return 0;
#endif
#ifdef ONLINE_JUDGE
	int n,h,d,i,j,hn,tmph;
	long long mymax = 0;
	cin >> n >> h >> d;
	for (i=1; i<=n; i++)
	{
		cin >> hn;
		for (j=1; j<=hn; j++)
		{
			cin >> tmph;
			mat[i][tmph]++;
		}
	}
	mymax = 0;
	for (i=1; i<=n; i++)								// 初始化树顶
	{
		dp[i][h] = mat[i][h];
		mymax = max(mymax, dp[i][h]);
	}
	hmax[h] = mymax;
	for (j=h-1; j>=0; j--)								// dp[i][j] = max(dp[i][j+1], hmax[j+d]);
	{
		mymax = 0;
		for (i=1; i<=n; i++)
		{
			if (j+d<=h)
			{
				dp[i][j] = mat[i][j] + max(dp[i][j+1], hmax[j+d]);
			}
			else
			{
				dp[i][j] = mat[i][j] + dp[i][j+1];
			}
			mymax = max(mymax, dp[i][j]);
		}
		hmax[j] = mymax;
	}
	mymax = 0;
	for (i=1; i<=n; i++)
	{
		mymax = max(mymax, dp[i][0]);
	}
	cout << mymax;
	return 0;
#endif
}


你可能感兴趣的:(NOI)