10.8 练习

T1:问题 A: 逃亡的准备

题目描述

在《Harry Potter and the Deathly Hallows》中,Harry Potter他们一起逃亡,现在有许多的东西要放到赫敏的包里面,但是包的大小有限,所以我们只能够在里面放入非常重要的物品,现在给出该种物品的数量、体积、价值的数值,希望你能够算出能使背包的价值最大的组合方式,并且输出这个数值,赫敏会非常地感谢你。

输入

第一行有 2 个整数,物品种数 n 和背包装载体积 v。
2 行到 i+1 行每行 3 个整数,为第 i 种物品的数量 m、体积 w、价值 s。

输出

包含一个整数,即为能拿到的最大的物品价值总和。

样例输入

2 10
3 4 3
2 2 5

样例输出

13

提示

【注释】

选第一种一个,第二种两个。结果为 3*1+5*2=13

【数据规模】

对于 30%的数据

1<=v<=500,1<=n<=2000,1<=m<=10,1<=w<=20,1<=s<=100

对于 100%的数据

1<=v<=500,1<=n<=2000,1<=m<=5000,1<=w<=20,1<=s<=100

题解

这道题是一道组合背包。可以考虑二进制压缩。而由于二进制可以拼成范围内的所有数,因此直接拆分成二进制的1,2,4,8……最后再来一个k。用01背包解决即可。

参考代码

#include<cstdio>
using namespace std;
int n,v,WEI[100001],VAL[100001];
int m,w,s,cnt=0,dp[100001],maxn=0;
int max1(int p,int q) { return p>q?p:q; }
int main()
{
	scanf("%d%d",&n,&v);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d%d",&m,&w,&s);
		int t=1;
		while(m>t)
		{
			m-=t;
			cnt++;
			WEI[cnt]=t*w;
			VAL[cnt]=t*s;
			t*=2;
		}
		if(m) 
		{
			cnt++;
			WEI[cnt]=m*w;
			VAL[cnt]=m*s;
		}
	}
	for(int i=1;i<=cnt;i++)
	{
		for(int j=v;j>=WEI[i];j--)
		{
			dp[j]=max1(dp[j],dp[j-WEI[i]]+VAL[i]);
		}
	}
	for(int i=1;i<=v;i++)
	maxn=max1(maxn,dp[i]);
	printf("%d",maxn);
	return 0;
}

T2:问题 B: 种树

题目描述

    现在我们国家开展新农村建设,农村的住房建设纳入了统一规划,统一建设,政府要求每一住户门口种些树。门口路边的地区被分割成块,并被编号成1..N。每个部分为一个单位尺寸大小并最多可种一棵树。每个居民房子门前被指定了三个号码B,E,T。这三个数表示该居民想在B和E之间最少种T棵树。当然,B≤E,居民必须记住在指定区不能种多于区域地块数的树,所以T≤E-B+l。居民们想种树的各自区域可以交叉。你的任务是求出能满足所有要求的最少的树的数量,尽量较少政府的支出。

输入

    第一行包含数据N,M,区域的个数(0<N≤30000),房子的数目(0<m≤5000);
    下面的m行描述居民们的需要:B E T,0<B≤E≤30000,T≤E-B+1。

输出

    输出第一行写有树的数目,下面的行包括所有树的位置,相邻两数之间用一个空格隔开。

样例输入

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

样例输出

5

题解

这道题需要用到差分约束系统。首先定义dp[i]表示前i块最少种多少树。显然有:0\leqslant dp[i+1]-dp[i]\leqslant 1,只取决于第i+1块种不种树。这样就可以得到2类不等式。注意要把n+1带入,最后记录答案直接输出dp[n+1]即可。然后对于b到e种至少t棵树,可以表示为:f[e]-f[b-1]\geqslant t。至于小于的不等式就没必要了,因为上两类不等式已经表达了相同的意思。由于求最小值,所以应该找最远路。用SPFA即可解决,注意DJ不能处理负权。(本题数据未要求输出位置,否则需要输出任意一条路径)。0也可以种树!

参考代码

#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
struct tree
{
	int nxt,to,dis;
}tr[90010];
queue<int>q;
int head[90010],cnt=0,d[90010];
int n,m,b,e,t,vis[90010];
void build_tree(int u,int v,int d)
{
	tr[++cnt].nxt=head[u];
	tr[cnt].to=v;
	tr[cnt].dis=d;
	head[u]=cnt;
}
int main()
{
	memset(d,-127,sizeof(d));
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&b,&e,&t);
		build_tree(b-1,e,t);
	}
	for(int i=0;i<=n;i++) 
	{
		build_tree(i+1,i,-1);
		build_tree(i,i+1,0);
	}
	q.push(0);vis[0]=1;d[0]=0;
	while(!q.empty())
	{
		int k=q.front();q.pop();
		vis[k]=0;
		for(int i=head[k];i;i=tr[i].nxt)
		{
			int to=tr[i].to;
			if(d[to]<d[k]+tr[i].dis)
			{
				d[to]=d[k]+tr[i].dis;
				if(!vis[to])
				{
					vis[to]=1;
					q.push(to);
				}
			}
		}
	}
	printf("%d",d[n+1]);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值