SWUST OJ 之 0195 Buyer

Tags: 动态规划

哆啦A梦班级举办个party,当然吃的东西必不可少,哆啦A梦负责采购任务,他得到了一份清单,上面注明不同食品的受欢迎程度,哆啦A梦需要用一定的价钱尽可能达到的更大的受欢迎程度!例如,瓜子的受欢迎程度为20,瓜子的价钱是50元,那么如果哆啦A梦选择买瓜子,将花费50元,但受欢迎程度增加了20。为了避免食品单调性,每种食品只能买一份,不能重复购买。 现在哆啦A梦需要知道如何采购才能达到最大的受欢迎程度,你能帮助他吗?

输入

输入数据为多组,每组输入的第一行有两个正整数M和N(M<100&&N<1000),分别为哆啦A梦可以支配的钱数和清单上

的可选择的物品种类。 接下来的N行每行有两个正整数,分别为每种物品的价钱和它的受欢迎程度(编号为1到N)。

输出

如果存在物品购买,那么输出的第一行为能够达到的最大的受欢迎程度。第二行为需要购买的物品的编号(如果有多种可能,输出字典序靠前的那种),空格分隔每个数字;如没有物品可以购买,输出只有一行,为数字0。

样例输入

10 4
100 5
5 5
5 5
10 10

样例输出

10
2 3

分析:

 

题目给出要求我们有 M 元可支配,有 N 个物品可选择,设每个物品的 价值 和可获得的 满足感 保存在结构体数组 List[ n ] 中对应下标位置空间上,最后我们要尽可能的将钱花完且满足感最大。

 

现在我们考虑一般情况,对每个物品 i 我们只有两种选择:买不起 <-- or --> 买得起
买不起 i :那么剩下钱数 j = j,最优满足感 degree[ i ][ j ] = degree[ i - 1 ][ j ];

 

买得起 i :那么剩下钱数 j = j - list[ i ].money,再用第 i 个物品的满足感加上剩下钱数所能满足的最优满足感,得到最优满足感 degree[ i ][ j ] = Max(degree[ i - 1 ][ j ] , degree[ i - 1 ][ j - list[ i ].money] + list[ i ].value);

现在我们考虑一下购买物品编号情况:
当买不起 i  <-- or --> 买得起没买 时,我们可以知道 degree[ i ][ j ] = degree[ i - 1 ][ j ],不记录 ;
当买得起 i <-- and -->买得起买了 时,我们可以知道 degree[ i ][ j ] > degree[ i - 1 ][ j ],记录 i 下标,将剩下钱数更新 j = j - list[ i ].money;

这样就构建一个解题思路。

代码:

// Text.cpp: 定义控制台应用程序的入口点。
//

#include
#include
#include
using namespace std;

int degree[1005][1005];     //map

typedef struct node         //创建结构体 List 存储花费值、满足感值
{
	int money;
	int value;
}List;

int Max(int a, int b)       //求最大值
{
	return a > b ? a : b;
}

int MaxValue(List *list, int n, int m)	//有 n 个物品,有 m 元
{
	int max0 = 0;
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= m; j++)
		{
			if (j - list[i].money < 0)  //剩余钱数 j 不足以买第 i 个物品
				degree[i][j] = degree[i - 1][j];
			else        //买第 i 个物品 + 剩下 j - list[i].money 钱数的最优解
				degree[i][j] = Max(degree[i - 1][j],
					degree[i - 1][j - list[i].money] + list[i].value);
			if (degree[i][j] > max0)    //更新最大值
				max0 = degree[i][j];
		}
	}
	return max0;
}

int FindDegree(int temp[], int i, int j, List *list)
{
	int k = 0;
	while (i)
	{
		if (degree[i][j] > degree[i - 1][j])    //若相同则没有买第 i 个物品,否则买了
		{
			temp[++k] = i;                      //将买了的物品标号记录下来
			j -= list[i].money;                 //更新剩余钱数
		}
		i--;                                    //剩余物品个数减一
	}
	return k;
}

int main()
{
	memset(degree, 0, sizeof(degree));		//置零
	int m, n;
	while (~scanf("%d %d", &m, &n))
	{
		List *list = new List[n + 1];       //动态创建一个结构体数组
		for (int i = 1; i <= n; i++)        //将下标与每个选择对齐
			scanf("%d %d", &list[i].money, &list[i].value);

		int maxDegree = MaxValue(list, n, m);//找最大满足感值
		printf("%d\n", maxDegree);

		int temp[1005];
		int k = FindDegree(temp, n, m, list);//得到购买的物品个数

		for (int i = k; i > 0; i--)         //记录的编号是降序的,所以倒着输出
		{
			if (i > 1)
				cout << temp[i] << " ";
			else
				cout << temp[i] << endl;
		}
	}
	return 0;
}

你可能感兴趣的:(SWUST,OJ题库,动态规划,算法练习,编程练习)