Yurchiu's Blog

P2347 砝码称重 等

Yurchiu 2020-01-16, 17:04:56 1.9k 隐藏左右两栏 展示左右两栏

本文为题解集

P2347 砝码称重
P5020 货币系统

来一个祭:

我太蒻了。

P2347 砝码称重

题目描述

设有 1g1g2g2g3g3g5g5g10g10g20g20g 的砝码各若干枚(其总重 1000\le1000 ),

输入格式

输入方式:a1a_1 , a2a_2 , a3a_3 , a4a_4 , a5a_5 , a6a_6

(表示 1g1g 砝码有 a1a_1 个,2g2g 砝码有 a2a_2 个,…,20g20g 砝码有 a6a_6 个)

输出格式

输出方式:Total=NTotal=N

NN 表示用这些砝码能称出的不同重量的个数,但不包括一个砝码也不用的情况)


暴力:

#include <bits/stdc++.h>
#define _ 0
using namespace std;
int v[1005],m[10],tmp=0,ans=0;
int main()
{
    for(int i=1;i<=6;i++)
    	scanf("%d",&m[i]);
    for(int a=0;a<=m[1];a++)
        for(int b=0;b<=m[2];b++)
            for(int c=0;c<=m[3];c++)// C++: ???
                for(int d=0;d<=m[4];d++)
                    for(int e=0;e<=m[5];e++)
                        for(int f=0;f<=m[6];f++)
                            tmp=a+b*2+c*3+d*5+e*10+f*20,v[tmp]=1;
    for(int i=1;i<=1000;i++)
    	ans+=v[i];
    printf("Total=%d",ans);
    return ~~(0^_^0);//嘤嘤嘤
}

然鹅,只有 72pts。对啊,66for 循环能不 TLE 吗?

一个思路:

设有 1g1g2g2g3g3g5g5g10g10g20g20g 的砝码各若干枚

即表示 1g1g 砝码有 a1a_1 个,2g2g 砝码有 a2a_2 个,…,20g20g 砝码有 a6a_6

emm…这不就是多重背包吗?

我们可以设有 maxmax 个背包,整齐地排成一行,分别可以承重 1,2,3,...,max1,2,3,...,\max(其中 max\max 指所有砝码的重量)。

跑一遍多重背包,数一下各个背包,有几个背包放满了砝码(bag[i]==i)!

补充更新:这其实是可行性 dp。状态转移方程的逻辑是:既然 bag[i] 是合法的,那么在 ii 的基础上再加一个砝码也是合法的。

#include<bits/stdc++.h>
#define _ 0
using namespace std;
int w[]={0,1,2,3,5,10,20},a[10],f[1005];
int main()
{
	int ans=0;
	for(int i=1;i<=6;i++)
		scanf("%d",&a[i]);
    for(int i=1;i<=6;i++)//dp,多重背包
	 	for(int j=1000;j>=w[i];j--)
    		for(int k=1;k<=a[i];k++)
    			if(j>=k*w[i])
	   				f[j]=max(f[j],f[j-k*w[i]]+k*w[i]);
	for(int i=1;i<=1000;i++)
		if(f[i]==i)
			ans++;
    printf("Total=%d",ans);
  	return ~~(0^_^0);//嘤嘤嘤
} 

P5020 货币系统

在网友的国度中共有 nn 种不同面额的货币,第 ii 种货币的面额为 aia_i,你可以假设每一种货币都有无穷多张

emm…类似地,你也可以把它转化为背包问题,基本思路与上题差不多!

code

po 一下 code\color{red}{\texttt{code}}

#include<bits/stdc++.h>
#define _ O//这次会CE!为什么?显而易见!
using namespace std;
int t,n,a[100+5],f[25000+5],ans,ins[25000+5];
void work()
{
	for(int i=1;i<=n;i++)
	 	for(int j=a[i];j<=a[n];j++)//小小剪枝,只需枚到货币面额的最大值。
	 	{
	 		f[j]=max(f[j],f[j-a[i]]+a[i]);
	 		if(f[j]==j&&f[j-a[i]]!=0)//f[j]的状态不是由空背包状态转移来,即不是被本面额表示
	 			ins[j]=1;
		}
	return;		
}	
int main()
{ 
	scanf("%d",&t);
	for(int i=1;i<=t;i++)
	{
		memset(f,0,sizeof(f)),memset(ins,0,sizeof(ins));//记得清0啊啊啊
		scanf("%d",&n),ans=n;
		for(int j=1;j<=n;j++)
			scanf("%d",&a[j]);
		sort(a+1,a+1+n),work();
		for(int j=1;j<=n;j++)
			ans-=ins[a[j]];//简写 等价于 if(ins[a[j]])ans--;
		printf("%d\n",ans);
	} 
	return ~~(0^_^0);//嘤嘤嘤
}
/*
此code有许多需改进的地方,比如需要排序。
访者可思考如何做可不用排序。
https://henryhuang.blog.luogu.org/solution-p5020
*/

思路

ins[]instead,是否可以被现有货币表示出,且不是只用一张货币。

f[]:背包。

证明:$ B \subseteq A$

设有两个货币系统 (n,a)(n,a)(m,b)(m,b)

首先,我们需证明:为什么不能添加新的货币面额,而只能减少现有货币面额种类?

即,设 (n,a)(n,a) 为集合 AA(m,b)(m,b) 为集合 BB,为什么 $ B \subseteq A$?

这里,引用大佬的证明,链接here

------------------------------以下为引用内容------------------------------

在证明这个猜测前我们先来证明另一个猜测 AA 集合内不能被其它数组成的数必然存在于 BB 集合内,这里使用反证法证明。


证明过程

我们设 xAx\in Axx 不能被 AA 集合内除它以外的元素组成。

然后我们假设 xBx \notin B ,那么就说明BB集合中必然存在一些元素能够组成 xx

那么这些元素至少存在一个不在集合 AA 内并且不能被集合 AA 里的元素组成的数(因为如果不存在的话集合 AA 内的元素就可以组成 xx了),可以看到这与集合 BB 的定义产生了矛盾。

综上所述, AA 集合内不能被其它数组成的数必然存在于 BB 集合内 。证毕。


通过这个结论我们便可以证明 BAB \subseteq A 这个猜测了。这里同样使用反证法证明。


证明过程

假设存在 xBx \in B 且满足 xAx \notin A

根据题目条件,xx 必然可以被 AA 集合内的数字所组成。

那么必然存在 a1,a2,...aqAa_1,a_2,...a_q \in A 可以用来组成 xx 并且这些元素都不能被 AA 集合内的元素组成(因为如果 aia_i 能被 AA 集合内其它数组成,那么必然可以用那些数来代替 aia_i),根据上一个结论我们可以知道 a1,a2,...aqBa_1,a_2,...a_q \in B

那么也就是说 xx 可以被 BB 集合内的数字组成,那么凡是可以用 xx 组成的数都可以被 BB 集合内的其它数字组成,那么也就是说即便从集合 BB 中去掉 xx 元素也依旧满足条件,这就与 BB 集合的定义矛盾。

所以对于任意 xBx \in B必然满足 xAx \in A,即 BAB \subseteq A


那么做这道题仅仅只需要最后一个证明了,如果你到这里都看懂了的话那应该不难想到最后一个猜测就是AA 集合中能被其它数组成的数必然不在 BB 集合内,事实上这个结论很显然,读者只需要稍微想想便可以想出来,这里就不写证明过程了。

------------------------------以上为引用内容------------------------------

emm…请你自己证AA 集合中能被其它数组成的数必然不在 BB 集合内吧。

借鉴 砝码称重 思路

这样,思路很清晰:

去掉在原货币系统中可被其他货币表示出的货币面额。

类似地,我们可以设有 (maxmin)(\max-\min) 个背包,整齐地排成一行,分别可以放 ¥min,(min+1),...,max\min,(\min+1),...,\max(其中 max\max 指最大面额,min\min 指最小面额)。

跑一遍完全背包,数一下各个背包,有几个背包放满了人民币(滑稽

ii 号背包放满了 ¥ii,且不是由一张人民币放满的,则 ¥ii 可以被其他货币表示,打个标记。

遍历 a[] 数组,如果被打了标记,则说明这个面额可以简化,ans--





本文作者:Yurchiu

本文链接:https://yz-hs.github.io/d2c2955f8a97/

版权声明:本博客中所有原创文章除特别声明外,均允许规范转载,转载请注明出处。所有非原创文章,按照原作者要求转载。


By Yurchiu.
其他物件杂物收纳
Hitokoto

Yurchiu 说,除了她以外的人都很强!嘤嘤嘤~~
博客信息
文章数目
158
最近更新
08-21
本站字数
350.6k
文章目录