ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【CCF Online3普及组】买表

2021-02-12 15:57:04  阅读:181  来源: 互联网

标签:凑出 Online3 int 手表 kk 背包 买表 CCF dp


【CCF Online3普及组】买表

(File IO): input:watch.in output:watch.out

题目描述

Jimmy 到 Symbol 的手表店买手表,Jimmy 只带了 n 种钱币,第 i种钱币的面额为 k_i元,张数为 a_i张。Symbol 的店里一共有 m块手表,第 i块手表的价格为 t_i 元。
Symbol 的手表店不能找零,所以 Jimmy 只能在凑出恰好的钱数时才能购买一块手表。现在对于店里的每块手表,Jimmy 想知道他能不能凑出恰好的钱数进行购买。

输入

第一行两个空格分隔的整数 n和 m表示钱币数与手表数。

接下来 n行每行两个空格分隔的整数 k_i,a_i表示钱币的面额和张数。

第 n+2行,共 m个用空格分隔的整数 t_i,表示每块手表的价格。

输出

一共 m行,对于第 i行,如果能凑出恰好的钱数购买第 i块手表则输出"Yes"(不含引号,下同),否则输出"No",注意只有首字母大写。

样例输入

3 5
1 2
5 1
6 3
3 19 21 1 7

样例输出

No
Yes
No
Yes
Yes

数据范围限制

对于 50%的数据,n≤10,m≤60,a_i≤20,k_i≤5000,t_i​≤250。

对于 100%的数据,1≤n≤200,1≤m≤10^5,1≤a_i​≤1000,1≤k_i​≤500000,0≤t_i≤500000。

提示

第二块手表 19=6×3+1=6×2+5+1×2,可以恰好凑出。

第四块手表 1=1×1,可以恰好凑出。

第五块手表 7=5+2×1=6×1+1,可以恰好凑出。

想当年,我五年级时,参加这个比赛,写了个暴搜,骗了个40分。这道题显然是一个动态规划的背包问题,但是如果写多重背包的话,那就只能拿50分,而这道题的正确写法应该是01背包+二进制优化。把面额同时当作重量和价值,再把多重背包转化成01背包,再加上二进制优化,再写一段动规,输出,就可以了。把多重背包转化成01背包大家应该都会,假如第一、二、三个数分别是重量、价值、数量,那么,例如:
3 4 4
可以转为
3 4 1
3 4 1
3 4 1
3 4 1
再把后面的1去掉,就变成01背包了。但这样还远远不够,我们还需要二进制优化。但是应该怎么优化呢?例如:
3 4 4
可以转为
3×1 4×1
3×2 4×2
3×1 4×1

3 4
6 8
3 4

再例如:
3 4 17
可以转为
3×1 4×1
3×2 4×2
3×4 4×4
3×8 4×8
3×2 4×2

3 4
6 8
12 16
24 32
6 8

为什么可以这样转呢?大家从中可以发现,经过拆分,我们依然可以组成数量为1 ~ 4(或14,在例子中)的情况,且不遗漏,也不会多算。是不是很神奇?观察第二个乘数,除了第一和最后一组,每一组都是前一组的两倍。大家知道,20+21+22+……+2n=2n+1-1,二进制优化就是根据这一个原理来做到不遗漏的(且让组数尽量小,来提高程序效率)。但是最后一组又是怎么回事?咱们来看看第二个例子。如果把第二个例子的最后一组删掉,我们就只能组成数量为1 ~ 15的情况。这时,还时还差2,才能组成数量为1~17的情况,这时,我们只需要加上一组3×2 4×2,就可以解决了。

#include<cstdio>
int nn,n,m,k[205],a[205],maxx,t[100005],kk[200005],dp[500005];
int max(int x,int y)
{
	return x>y?x:y;
}
int main()
{
	freopen("watch.in","r",stdin);
	freopen("watch.out","w",stdout);
	scanf("%d%d",&nn,&m);
	for(int i=1;i<=nn;++i)
	{
		scanf("%d%d",&k[i],&a[i]);
	}
	for(int i=1;i<=m;++i)
	{
		scanf("%d",&t[i]);
		if(t[i]>maxx)//求m个手表的最大价格
		{
			maxx=t[i];
		}
	}
	for(int i=1;i<=nn;++i)
	{
		int p=1;//二进制优化,p就是第二个乘数
		while(a[i]>=p)//判断是否还能拆分
		{
			if(p*k[i]>maxx) break;//如果面额大于手表的最大价值,那这张纸币就没用了
			n++;
			kk[n]=p*k[i];//把经过处理的数放到新的数组里,kk即表示“重量”,也表示“价值”(题目中不是重量和价值)
			a[i]-=p;//减上p,做到不多算
			p*=2;
		}
		if(a[i]>0&&a[i]*k[i]<=maxx)//处理最后一组
		{
			n++;
			kk[n]=a[i]*k[i];
		}
	}
	for(int i=1;i<=n;++i)//动规
	{
		for(int j=maxx;j>=kk[i];--j)
		{
			dp[j]=max(dp[j],dp[j-kk[i]]+kk[i]);
		}
	}
	for(int i=1;i<=m;++i)
	{
		if(dp[t[i]]==t[i])//如果可以组成
		{
			printf("Yes\n");
		}
		else
		{
			printf("No\n");
		}
	}
}

不见不散(新年好!!!)

标签:凑出,Online3,int,手表,kk,背包,买表,CCF,dp
来源: https://blog.csdn.net/Wu_tongchun/article/details/113791814

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有