本文为题解集
P2347 砝码称重
P5020 货币系统
祭
来一个祭:
- 屈指可数的一遍过的题——P5020 货币系统
我太蒻了。
P2347 砝码称重
题目描述
设有 、、、、、 的砝码各若干枚(其总重 ),
输入格式
输入方式: , , , , ,
(表示 砝码有 个, 砝码有 个,…, 砝码有 个)
输出格式
输出方式:
( 表示用这些砝码能称出的不同重量的个数,但不包括一个砝码也不用的情况)
暴力:
#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。对啊, 层 for
循环能不 TLE 吗?
一个思路:
设有 、、、、、 的砝码各若干枚
即表示 砝码有 个, 砝码有 个,…, 砝码有 个
emm…这不就是多重背包吗?
我们可以设有 个背包,整齐地排成一行,分别可以承重 (其中 指所有砝码的重量)。
跑一遍多重背包,数一下各个背包,有几个背包放满了砝码(bag[i]==i
)!
补充更新:这其实是可行性 dp。状态转移方程的逻辑是:既然
bag[i]
是合法的,那么在 的基础上再加一个砝码也是合法的。
#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 货币系统
在网友的国度中共有 种不同面额的货币,第 种货币的面额为 ,你可以假设每一种货币都有无穷多张。
emm…类似地,你也可以把它转化为背包问题,基本思路与上题差不多!
code
po 一下 :
#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$
设有两个货币系统 和 。
首先,我们需证明:为什么不能添加新的货币面额,而只能减少现有货币面额种类?
即,设 为集合 , 为集合 ,为什么 $ B \subseteq A$?
这里,引用大佬的证明,链接here:
------------------------------以下为引用内容------------------------------
在证明这个猜测前我们先来证明另一个猜测 集合内不能被其它数组成的数必然存在于 集合内,这里使用反证法证明。
证明过程
我们设 且 不能被 集合内除它以外的元素组成。
然后我们假设 ,那么就说明集合中必然存在一些元素能够组成 。
那么这些元素至少存在一个不在集合 内并且不能被集合 里的元素组成的数(因为如果不存在的话集合 内的元素就可以组成 了),可以看到这与集合 的定义产生了矛盾。
综上所述, 集合内不能被其它数组成的数必然存在于 集合内 。证毕。
通过这个结论我们便可以证明 这个猜测了。这里同样使用反证法证明。
证明过程
假设存在 且满足 。
根据题目条件, 必然可以被 集合内的数字所组成。
那么必然存在 可以用来组成 并且这些元素都不能被 集合内的元素组成(因为如果 能被 集合内其它数组成,那么必然可以用那些数来代替 ),根据上一个结论我们可以知道 。
那么也就是说 可以被 集合内的数字组成,那么凡是可以用 组成的数都可以被 集合内的其它数字组成,那么也就是说即便从集合 中去掉 元素也依旧满足条件,这就与 集合的定义矛盾。
所以对于任意 必然满足 ,即 。
那么做这道题仅仅只需要最后一个证明了,如果你到这里都看懂了的话那应该不难想到最后一个猜测就是在 集合中能被其它数组成的数必然不在 集合内,事实上这个结论很显然,读者只需要稍微想想便可以想出来,这里就不写证明过程了。
------------------------------以上为引用内容------------------------------
emm…请你自己证在 集合中能被其它数组成的数必然不在 集合内吧。
借鉴 砝码称重 思路
这样,思路很清晰:
去掉在原货币系统中可被其他货币表示出的货币面额。
类似地,我们可以设有 个背包,整齐地排成一行,分别可以放 ¥(其中 指最大面额, 指最小面额)。
跑一遍完全背包,数一下各个背包,有几个背包放满了人民币(滑稽
若 号背包放满了 ¥,且不是由一张人民币放满的,则 ¥ 可以被其他货币表示,打个标记。
遍历 a[]
数组,如果被打了标记,则说明这个面额可以简化,ans--
。
本文作者:Yurchiu
本文链接:https://yz-hs.github.io/d2c2955f8a97/
版权声明:本博客中所有原创文章除特别声明外,均允许规范转载,转载请注明出处。所有非原创文章,按照原作者要求转载。