洛谷P1064题解

原题

P1064 [NOIP2006 提高组] 金明的预算方案


思路概述

题意分析

给定一个体积为 \(n\) 的背包和 \(m\) 个物品。每个物品通过 \((\text{价值},\text{体积})\) 的形式表示为 \((p_i·v_i,v_i)\),同时部分物品还对应了唯一的一个前驱 \(q_i\),只有前驱被选入背包的时候该物品才能被选取。求最后能取得的最大价值。

思路简述

有点意思的01背包变式,对于一个没有对应主件也没有对应附件的物品,操作策略与01背包无异;对于相互对应的主件和附件,由于每个主件最多两个附件(为方便表述,笔者称相互对应的主件与附件为一组物品),其所有的选取情况只有最多四种:只选取主件、选取主件和第一个附件、选取主件和第二个附件、同时选取主件和所有附件。


算法实现

关于背包的第二重循环

由于本题中一组物品的所有选取方式对应了多个不同的体积,所以此处就不需要将第二重循环设置为 \(V→v_i\),而是设为 \(V→0\)

关于多种状态的选取

由于一组物品的多个体积的选取过程中可能出现 \(V-v_i<0\) 的情况,所以需要在选取过程中判定这一点。为方便编码,笔者选择把这一步封装成自定义函数。


AC code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<set>
#include<ctime>
#define RI register int
using namespace std;
const int maxn=4e4+10;
typedef struct
{
	int w,v,pre;
	int nex[3];
}object;
typedef struct
{
	int w,v;
}node;
typedef struct
{
	int l,r;
}range;
object obj[maxn];
node e[maxn<<2];
range rg[maxn];
int n,m,cnt;
int f[maxn<<4];
inline void init(void);
inline int calc(int x,int v);
int main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin >> n >> m;n/=10;
	for(RI i=1;i<=m;++i)
	{
		cin >> obj[i].v >> obj[i].w >> obj[i].pre;
		obj[i].w*=obj[i].v;obj[i].v/=10;
		if(obj[i].pre) obj[obj[i].pre].nex[++obj[obj[i].pre].nex[0]]=i;
	}
	init();
	for(RI i=1;i<=m;++i)
		if(rg[i].l && rg[i].r)
			for(RI j=n;j>=0;--j)
				f[j]=max(f[j],calc(i,j));
	printf("%d",f[n]);
	fclose(stdin);fclose(stdout);
	return 0;
}
inline void init(void)
{
	/*将一个主件及其附件拆为多个单独的物品 拆分出的物品继承主附件的权值与体积和*/
	/*同时开空间存储当前主件及其附件所对应的物品下标*/
	for(RI i=1;i<=m;++i)
		if(obj[i].pre) continue;
		else if(!obj[i].nex[0]) 
		{
			e[++cnt]=(node){obj[i].w,obj[i].v};
			rg[i]=(range){cnt,cnt};
		}
		else if(obj[i].nex[0]==1)
		{
			e[++cnt]=(node){obj[i].w,obj[i].v};
			e[++cnt]=(node){obj[i].w+obj[obj[i].nex[1]].w,obj[i].v+obj[obj[i].nex[1]].v};
			rg[i]=(range){cnt-1,cnt};
		}
		else
		{
			e[++cnt]=(node){obj[i].w,obj[i].v};
			e[++cnt]=(node){obj[i].w+obj[obj[i].nex[1]].w,obj[i].v+obj[obj[i].nex[1]].v};
			e[++cnt]=(node){obj[i].w+obj[obj[i].nex[2]].w,obj[i].v+obj[obj[i].nex[2]].v};
			e[++cnt]=(node){obj[i].w+obj[obj[i].nex[1]].w+obj[obj[i].nex[2]].w,obj[i].v+obj[obj[i].nex[1]].v+obj[obj[i].nex[2]].v};
			rg[i]=(range){cnt-3,cnt};
		}
	return;
}
inline int calc(int x,int v)
{
	RI ret=0;
	for(RI i=rg[x].l;i<=rg[x].r;++i)
		if(v>=e[i].v)/*考虑是否买得下当前的物品*/
			ret=max(ret,f[v-e[i].v]+e[i].w);/*若买得下 就更新状态*/ 
	return ret;
}

posted @ 2022-10-26 21:54  UOB  阅读(78)  评论(0)    收藏  举报