买表(【CCF】NOI Online能力测试3 入门组)

   日期:2020-05-27     浏览:239    评论:0    
核心提示:题目描述Jimmy 到 Symbol 的手表店买手表,Jimmy 只带了nn种钱币,第ii种钱币的面额为 ki​元,张数为 ai​张。Symbol 的店里一共有m块手表,第i块手表的价格为 ti​元。Symbol 的手表店不能找零,所以 Jimmy 只能在凑出恰好的钱数时才能购买一块手表。现在对于店里的每块手表,Jimmy 想知道他能不能凑出恰好的钱数进行购买。输入格式第一行两个空格分隔的整数n和m表示钱币数与手表数。接下来nn行每行两个空格分隔的整数 ...

 

题目描述

Jimmy 到 Symbol 的手表店买手表,Jimmy 只带了 nn 种钱币,第 ii 种钱币的面额为 ki​ 元,张数为 ai​ 张。Symbol 的店里一共有 m 块手表,第 i 块手表的价格为 ti​ 元。

Symbol 的手表店不能找零,所以 Jimmy 只能在凑出恰好的钱数时才能购买一块手表。现在对于店里的每块手表,Jimmy 想知道他能不能凑出恰好的钱数进行购买。

输入格式

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

接下来 nn 行每行两个空格分隔的整数 ki​ 和 ai​ 表示钱币的面额和张数。

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

输出格式

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

输入样例

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

输出样例

No
Yes
No
Yes
Yes

样例解释

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

解法1:

思路分析:此题转化为子集和问题即可解决 

C++代码如下:

#include <iostream>
using namespace std;
int x=0,q[100000010],n,m,nn=0;//n为钱币有多少种种类 ,nn为钱币总张数

void FixedSum(int* a, int num, int t, int sum)
{
   if(sum == 0)
        x=1;
    else
    {
        if(t == num)
            return ;
        else
        {
            if(sum - a[t] >= 0)
                FixedSum(a, nn, t + 1, sum - q[t]) ;
            if(sum >= 0)
                FixedSum(a, nn, t + 1, sum) ;
        }
    }
}

int main()
{ 
	cin>>n>>m;
	int k,a;
	for(int i=0;i<n;i++)
	{
	    cin>>k>>a;
		for(int j=0;j<a;j++)//展开储存,如例中6,3转为6,6,6储存
		{
			q[nn]=k;
			nn++;
		}
	}
	for(int i=0;i<m;i++)
	{
		int sum;
		cin>>sum;
		FixedSum(q, nn, 0, sum);
		if(x)
			cout<<"Yes"<<endl;
		else
			cout<<"No"<<endl;
		x=0;
	}
	return 0; 
}
 

 

解法2: 

思路分析:

这个题目就是变形的多重背包,那么不难想到,用一个 5×10^5 的 bool 数组来 dp 每个数能否被选中。

但是这样极限数据是 200×1000×5×10^5,直接爆炸,只能拿 50 pts。

接下来就是套路性的优化多重背包了:二进制优化

比如有 a 个价值为 b 的物品,那么他们能组合出的就是 1×b,2×b,…a×b 价值的物品。

那么能不能构造另一些物品,让这些物品同样能组合出 1×b,2×b,…a×b 价值的物品呢?

可以的。

我们需要构造的是价值为 1×b,2×b,4×b,…2^n×b,其中这些物品价值的总和不超过a×b 且 n 尽量大。当然最后离 a×b 可能还有一些距离,所以要用一个价值为 (a−∑i=1n​2n)×b 来弥补。

用二进制的位值原理法可以得出这个是正确的。首先,除最后的一个物品可以组合出 价值 1×b∼(2n+1−1)×b 价值的物品。然后再搭配最后一个物品就可以组合出全部物品了。

好处是物品数量由 a 下降到了 loga,但是 200×10×5×105 似乎还是过不了 

那么还有一个好东西:bitset

上面的 dp 看起来是这个样子的:

for(j=5e5-l*k;j>=0;j--)
	if(dp[j])
		dp[j+l*k]=1;

 

那不就是位运算吗?说清楚一些,如果把 dp 数组当作一个大 bool 类型,那么这个操作就等同于 dp|=dp<<l*k

正好,万能的 STL 库中就有这个好东西 bitset,本质上是一个 bool 数组(内置是用 ull 压了位),但是仍然支持各种位运算。

这样定义:

bitset<500005> dp;

操作也很简洁:

dp|=dp<<l*k;

AC的C++代码如下: 

#include<bits/stdc++.h>
using namespace std;
bitset<500005> dp;
int main()
{
	int n,m,i,j,a,k,t;
	cin>>n>>m;
	dp[0]=1;
	for(i=0;i<n;i++)
	{
		cin>>k>>a;
		for(j=1;a>=j;j*=2)
		{
			dp|=dp<<j*k;
			a-=j;
		}
		if(a*k)
			dp|=dp<<a*k;	
	}
	while(m--)
	{
		cin>>t;
		if(dp[t])
			cout<<"Yes"<<endl;
		else 
			cout<<"No"<<endl;
	}
	return 0;
}

 

 
打赏
 本文转载自:网络 
所有权利归属于原作者,如文章来源标示错误或侵犯了您的权利请联系微信13520258486
更多>最近资讯中心
更多>最新资讯中心
0相关评论

推荐图文
推荐资讯中心
点击排行
最新信息
新手指南
采购商服务
供应商服务
交易安全
关注我们
手机网站:
新浪微博:
微信关注:

13520258486

周一至周五 9:00-18:00
(其他时间联系在线客服)

24小时在线客服