砝码称重满分解法
一、完整题目
二、解题思路
这道题是一道动态规划问题,基本概念与解法可参考:动态规划算法总结
就此道题而言,具体思路为:
假设一共有n个砝码,在放入n-1个砝码时一共可以称出f[n-1]种重量,所以,再放入第n个砝码时,又会增加f[w[n]-w[i]]和f[w[n]+w[i]]两种情况,只需要将每轮新放入的砝码与上一轮可能出现的重量做差和求和,并记录下来即可。
考虑会发生重复的现象,我们要将可称出的重量的值置为1,所以及时出现重复的现象,值也始终为1,不影响结果。
同时还要考虑内存的限制,如果按照正常动态规划的做法,需要开一个f[100][100000]这么大的数组,很显然是不可以的,程序跑不起来,所以我们要对一般的动态规划算法进行改进,我们定义两个数组f[100000]和g[100000]互相存储上一轮的结果,可以节省很大的内存。
三、编写代码
1.一般方法
#include <stdio.h>
#include <math.h>
#define M 100
#define N 100000
int main()
{
int i,j,n,w[M],sum=0,x,cnt=0;
int f[M][N]={0};
/*输入*/
scanf("%d",&n);
for(i=0;i<n;i++) {scanf("%d",&w[i]);sum+=w[i];}
/*计算*/
f[0][w[0]]=1;//第一个砝码重量
for(i=1;i<n;i++)//从第二个砝码开始
{
x=w[i];//此轮砝码重量
for(j=1;j<=sum;j++)//将上一轮结果抄入
{
f[i][j]=f[i-1][j];
}
f[i][x]=1;//此轮加入的砝码
for(j=1;j<=sum;j++)//组合
{
if(f[i-1][j])
{
f[i][j+x]=1;
f[i][abs(j-x)]=1;
}
}
}
for(i=1;i<=sum;i++)//统计数量
{
if(f[n-1][i]) cnt++;
}
printf("%d",cnt);
return 0;
}
可以看出所占内存空间特别大,这种解法拿不到满分,因此按照上述解题思路所言进行改进。
2.改进方法
#include <stdio.h>
#include <math.h>
#define M 100
#define N 100000
int main()
{
int i,j,n,w[M],sum=0,x,cnt=0;
int f[N]={0},g[N]={0};
/*输入*/
scanf("%d",&n);
for(i=0;i<n;i++) {scanf("%d",&w[i]);sum+=w[i];}
/*计算*/
f[w[0]]=1;//第一个砝码重量
for(i=1;i<n;i++)//从第二个砝码开始
{
x=w[i];//此轮砝码重量
if(i%2==1)
{
for(j=1;j<sum;j++)//将上轮结果抄入
{
g[j]=f[j];
}
for(j=1;j<=sum;j++)//组合
{
if(f[j])
{
g[j+x]=1;
g[abs(j-x)]=1;
}
}
g[x]=1;//此轮加入的砝码
}
else{
for(j=1;j<sum;j++)//将上轮结果抄入
{
f[j]=g[j];
}
for(j=1;j<=sum;j++)//组合
{
if(g[j])
{
f[j+x]=1;
f[abs(j-x)]=1;
}
}
f[x]=1;//此轮加入的砝码
}
}
for(i=1;i<=sum;i++)//统计数量
{
if((n-1)%2==0)
{
if(f[i]) cnt++;
}
else{
if(g[i]) cnt++;
}
}
printf("%d",cnt);
return 0;
}
可以完美且正确的得到结果测评结果如下。
四、测评结果
五、总结评价
在内存有限制的时候,两个一维数组往往比一个二维数组更加有效。
有问题欢迎各位大佬指出
蓝桥杯系列将持续更新,欢迎关注,一起学习