【t038】&&【u214】金字塔

Time Limit: 1 second
Memory Limit: 128 MB

【问题描述】

小X来到一个雄奇的金字塔挖宝,但是这是一座被诅咒的金字塔,小X必须马上逃离这里,否则小X就会被埋在金字塔里,但他不希望此行落空。 
现在小X面前有N+1种财宝,每种财宝都有一个价值。第一种财宝重量为0,第二种财宝重量为1,总之第I种财宝重量为I-1。现在小X希望
拿走N+M个物品,但是这M+N个物品总重量不能超过N。小X希望能获得最大的价值。你能帮帮他吗?
由于金字塔跟小X一样牛,所以每种财宝无限个。


【输入格式】

第一行两个正整数N,M 第二行N+1个整数,第I个整数代表了第I种财宝的价值

【输出格式】

一个数,表示最大利润。

【数据规模】

10%满足N,M<=10 40%满足N,M<=100 100%满足 N,M<=3000 abs(财宝价值)<=1000

Sample Input1

5 3
4 7 2 5 -3 6




Sample Output1

47

【题解】

这题有两个很烦人的地方,一个是m+n个物品的限制。一个是重量为0的物品。

我们要怎么去除掉这个问题呢。

首先考虑一下最后我们选取的物品。

因为规定了要选m+n个物品。如果不选重量为0的物品可能会出现选不够的问题。

因此必然是掺杂着重量为0和重量不为0的物品。设为x,y.

设重量为0的物品下标为0,重量不为0的物品下标为1..n;

c是物品价值数组,w是物品的重量数组。

最后的答案 == x*c[0] + y*c[0] +∑(c[i]) - y*c[0]  (其中的求和为所选出的最优的y个有重量的物品的价值的和);

但是先别急着划掉两个y*c[0];

我们合并一下。ans == (x+y)*c[0] + ∑(c[i]) - y*c[0];

其中x+y==n+m;

ans == (n+m)*c[0] + ∑(c[i]) - y*c[0];

其中左边绿色部分为常量,右边为需要求的量。

因为∑(c[i])一共求和了y(未知)个c[i],则可以再化一下。

ans == 常量 + ∑(c[i]-c[0]) i∈【1,n】

这下问题就变成求物品重量为w[i],价值为c[i]-c[0]的完全背包问题了。

右边最大则ans最大。

还有一个优化的方法。即如果c[i]-c[0]是小于0的,那么这个物品就不要了。

因为如果c[i]比c[0]还小,那么拿不用重量的物品来替代i物品肯定最优。(c[0]不用消耗重量!)

【代码】

#include <cstdio>

int n,m,temp,num = 0,f[3011],w[3011],c[3011];

void input_data()
{
	scanf("%d%d",&n,&m);
	scanf("%d",&temp); //读入c[0] 
	for (int i = 1;i <= n;i++)
		{
			int x;
			scanf("%d",&x); 
			x -= temp; //输入第i个物品的价值,先减去c[0] 
			if (x > 0) //如果减去后还大于0则这个物品是可能在y个物品中的。 
				{
					num++;
					w[num] = i;
					c[num] = x;
				}
		}
}

void get_ans()
{
	for (int i = 1;i <= num;i++) //对c[i]-c[0] (i∈[1..n]) 进行完全背包的过程 
		for (int j = w[i];j <= n;j++)
			if (f[j] < f[j-w[i]] + c[i])
				f[j] = f[j-w[i]] + c[i];
}

void output_ans()
{
	printf("%d",f[n] + (n+m)*temp);	//最后输出f[n]的时候还要加上被减去的y个c[0]。以及x个c[0]; 
}

int main()
{
	//freopen("F:\rush.txt","r",stdin);
	input_data();
	get_ans();
	output_ans();
	return 0;	
}



原文地址:https://www.cnblogs.com/AWCXV/p/7632373.html