金明的预算方案题解

注意:此篇题解作者文字表述力极差思路极为混乱。请谨慎阅读,有不严谨或不准确的地方可以提出建议,谢谢。

注意:此题解同样适用于附件个数最多为59个的升级版中。

注意:此题解文字描述内部分变量类型是错误的(这里指long long开成了int),一切变量类型以完整代码为准。

注意:此题解完整代码与文字内容仅仅是思路相同,变量名存在不统一现象。

关于文字描述与完整代码之间的数组名称的互相转换
文字描述完整代码
aB
bA
dpDP

 

 


基础分析

价值=价格*重要程度
最终要求的是我们买的物品所有价值之和且最大


思路

首先,我们的附件必须要依托于主件
我们可以先跳过附件
只对主件进行处理
我们需要知道这个主件的有哪些附件,这些附件的重要程度和价格

这时我们可以用一个结构体存放物品信息。

结构体 struct

struct node{
   int v;//价格
   int w;//价值(重要程度*价格)
};
node a[100][100000];
a[i][j]的含义是:
(假设第i件物品一定为主件)
a[i][j]就是第i件物品的第j件附件
int num[100]//用于存放每个下标为i的主件的附件个数
node b[100];//下标为i的主件的信息

输入时输入n,m
n指的是我们拥有的钱数
m指的是想要的物品个数

然后我们通过判断输入的第三个数也就是题目中的q,即类型,选择是将其存入b数组,还是存入a数组并num中对应的值++。
然后我们进行dp操作
int dp[100][40000];//dp在这里的含义是dp[i][j]是指前i个物品中总价格为j
的几件或一件物品的价值。

dp操作
首先判断是否为主件,如果不是,将上一层的dp数组顺承到下一行

可能不太严谨,能理解就行


for(int j=1;j<=n;j++)
{
    dp[i][j]=dp[i-1][j];
}


如果是的话,我们首先把

dp[i][b[i].v]=b[i].w;

因为我们这时一定会选这个主件,所以我们需要将其转移过来。
接下来我们来dp这个主件的附件
由于我们用的是01背包,而且为了节约空间,我们删去了一维数组,所以我们需要倒序循环。当我们选择一个附件,且此附件相对应的位置已经被存过,就可以进行转移

dp[i][j]=max(dp[i][j],dp[i][j-a[i][k].v]+a[i][k].w);

for(int k=1;k<=num[i];k++)
{
        for(int j=m;j>=a[i][k].v;j--)
        {
                if(dp[i][j-a[i][k].v]!=0)
                {
                        dp[i][j]=max(dp[i][j],dp[i][j-a[i][k].v]+a[i][k].w);
                }
   }
}

因为我们在转移过程中有些附件它不能买,或一些位置没有填上,所以我们需要取这一排和前一排的最大值,来补上一些没有值的位置。

for(int j=1;j<=n;j++)
{
    dp[i][j]=max(dp[i][j],dp[i-1][j]);
}

最后我们输出dp[n][m]就行了。


完整代码

#include<bits/stdc++.h>
using namespace std;
#define N 65
#define M 42005
struct node
{
	long long v,w;
}A[N];
struct node B[N][N]; 
int n,m,num[N];
long long DP[N][M];

int main()
{
	scanf("%d%d",&m,&n);
	for(int i=1;i<=n;i++)
	{
		int aa,bb,cc;
		scanf("%d%d%d",&aa,&bb,&cc);//价格 重要度 类型 
		bb*=aa;//求出真·价值
		//这时a是价格
		//b是价值 
		if(cc==0)
		{
			A[i].v=aa;
			A[i].w=bb;
		}
		else
		{
			B[cc][++num[cc]].v=aa;
			B[cc][num[cc]].w=bb;
		}
	}
	for(int i=1;i<=n;i++)
	{
		if(A[i].v==-1)
		{
			for(int j=0;j<=m;j++)
			{
				DP[i][j]=DP[i-1][j];
			}
		}
		else
		{
			DP[i][A[i].v]=A[i].w;
			for(int j=A[i].v;j<=m;j++)//A[i].v+1??? 
			{
				DP[i][j]=max(DP[i][j],DP[i-1][j-A[i].v]+A[i].w);
			}
			for(int k=1;k<=num[i];k++)
			{
				for(int j=m;j>=B[i][k].v;j--)
				{
					if(DP[i][j-B[i][k].v]!=0)
					{
						DP[i][j]=max(DP[i][j],DP[i][j-B[i][k].v]+B[i][k].w);
					}
				}
			}
			for(int j=0;j<=m;j++)
			{
				DP[i][j]=max(DP[i][j],DP[i-1][j]);
			}
		}
	}
	printf("%lld",DP[n][m]);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值