试题题目:
本题为编程题第二题
解题思路:
方法一:暴力求解
1、分析
首先利用数组
W
[
N
]
W[N]
W[N]记录
N
N
N块砝码的质量。
模拟放取过程。先取第一块砝码放在天平上,再取第二块砝码,有三种情况选择:(1) 不放;(2) 放左边;(3) 放右边。第二块砝码摆放完成后,就能获得4种质量:(1) 不放砝码时:原质量;(2) 砝码放左边时:原质量减去第二块砝码质量的绝对值;(3) 砝码放右边时:原质量加上第二块砝码总质量;(4) 第二块砝码本身的质量。
再上一步的基础上,取第三块砝码开始摆放,每种质量下也同样有三种情况。那么再第三块砝码摆放完成后,会得到3x4+1种质量。“+1”的原因是第三块砝码本身质量。
依次循环类推,摆放完最后一块砝码后,将获得
(
3
N
−
1
)
/
2
(3^N-1)/2
(3N−1)/2种质量。但是当
N
N
N的数很大时,质量种数也会非常庞大。所以在每取完一个砝码得出质量后,对质量数组进行去重处理,取下一个砝码时计算质量时不会被重复计算。
2、代码编写
(1)、数组
W
[
N
]
W[N]
W[N]记录
N
N
N块砝码的质量;
(2)、循环取砝码,用二维数组
V
[
i
]
[
j
]
V[i][j]
V[i][j]记录放第i块砝码的质量;
α
\alpha
α、
V
[
i
]
[
k
+
+
]
V[i][k++]
V[i][k++] =
V
[
i
−
1
]
[
j
]
V[i-1][j]
V[i−1][j] ;
β
\beta
β、
V
[
i
]
[
k
+
+
]
V[i][k++]
V[i][k++] =
V
[
i
−
1
]
[
j
]
V[i-1][j]
V[i−1][j]
+
+
+
W
[
i
]
W[i]
W[i] ;
γ
\gamma
γ、
V
[
i
]
[
k
+
+
]
V[i][k++]
V[i][k++] =
V
[
i
−
1
]
[
j
]
V[i-1][j]
V[i−1][j]
−
-
−
W
[
i
]
W[i]
W[i] ;
δ
\delta
δ、
V
[
i
]
[
k
+
+
]
V[i][k++]
V[i][k++] =
W
[
i
]
W[i]
W[i] ;
(3)、去重处理,遍历
V
[
i
]
[
j
]
V[i][j]
V[i][j],使得
V
a
l
u
e
[
V
[
i
]
[
j
]
]
=
1
Value[V[i][j]]=1
Value[V[i][j]]=1;
(4)、对
V
[
i
]
[
j
]
V[i][j]
V[i][j]重新赋值,使得第i个数组内没有重复值。进入下一次循环,摆放下一个砝码。
个人题解C语言代码如下:
#include<stdio.h>
int N;
int W[105];
int V[105][100006];
int main(){
int i,j,times = 1;
scanf("%d",&N);
for(i=0;i<N;i++){
scanf("%d",&W[i]);
}
int Wight=W[0];
V[0][0] =W[0];
for(i=1;i<N;i++){
Wight = Wight+W[i];
int z,t=0,k = 0;
int Value[Wight+3];
for(z=0;z<Wight+3;z++){
Value[z]=0;
}
for(j=0;j<times;j++)
{
V[i][k++] = V[i-1][j];
V[i][k++] = V[i-1][j] + W[i];
V[i][k++] = V[i-1][j] - W[i];
}
V[i][k] = W[i];
for(z=0;z<k+1;z++){ //去重处理
if(V[i][z]<0){
Value[0-V[i][z]]=1;
}
else Value[V[i][z]]=1;
}
for(z=1;z<Wight+1;z++){
if(Value[z]==1){
V[i][t++]=z;
}
}
times = t;
}
printf("%d",times);
return 0;
}
优化代码,并使用算法提高代码运行速度与质量,采用动态规划思想。将上述代码中,不在统计计算出的质量,且不用数组 V a l u e [ ] Value[] Value[]中间转换的方法来去重,而是直接利用下标作为质量,直接为二维数组赋值,1或者0;1表示可以表示出下标的质量,反之0不能表示。
方法二:动态规划
1、思考
看到样例说明中有这种参差不齐感觉的时候,想到的必然是使用算法来解决问题了,当然使用暴力求解也是可以的。对于砝码称重问题,考虑放砝码还是不放砝码,想到的是
“
“
“放还是不放
”
”
”,
“
“
“取还是不取
”
”
”等类似问题,所以使用的动态规划算法。
2、分析
动态规划问题首先要确定状态,再确定状态转移方程,然后考虑边界初始化。
(1)、状态: d p [ i ] [ j ] dp[i][j] dp[i][j]表示前 i i i个砝码能否表示出质量 j j j,能则变量值为 1 1 1,不能则为 0 0 0。
(2)、状态转移方程:
d
p
[
i
]
[
j
]
=
d
p
[
i
]
[
j
+
a
[
i
]
]
=
d
p
[
i
]
[
j
−
a
[
i
]
]
=
1
dp[i][j] =dp[i][j+a[i]]=dp[i][j-a[i]]= 1
dp[i][j]=dp[i][j+a[i]]=dp[i][j−a[i]]=1
需要首先判断: 前
i
−
1
i-1
i−1 个砝码都能否表示出质量
j
j
j 。
若不能,那么第
i
i
i个砝码放在哪里都不合理,没有这个砝码都不能表出质量,再放一个该砝码,更不能确定前
i
i
i个砝码能表示出来的质量;
若能,那么前
i
i
i个砝码也一定能够表示出质量
j
j
j。则第
i
i
i个砝码也能放或不放,在这个基础之上在分别讨论第
i
i
i个砝码放在左边还是放右边,讨论情况如下:
α
\alpha
α. 不放第
i
i
i个砝码。那前
i
−
1
i-1
i−1能表示出质量
j
j
j,前
i
i
i个砝码也能表示出质量
j
j
j:
d
p
[
i
]
[
j
]
=
1
dp[i][j]=1
dp[i][j]=1;
β
\beta
β. 放第
i
i
i个砝码,在右侧。那前
i
−
1
i-1
i−1能表示出质量
j
j
j,前
i
i
i个砝码也能表示出质量
j
+
w
[
i
]
j+w[i]
j+w[i]:
d
p
[
i
]
[
j
+
w
[
i
]
]
=
1
dp[i][j+w[i]]=1
dp[i][j+w[i]]=1;
γ
\gamma
γ. 放第
i
i
i个砝码,在左侧。那前
i
−
1
i-1
i−1能表示出质量
j
j
j,前
i
i
i个砝码也能表示出质量
j
+
w
[
i
]
j+w[i]
j+w[i]:
d
p
[
i
]
[
j
−
w
[
i
]
]
=
1
dp[i][j-w[i]]=1
dp[i][j−w[i]]=1;注意
j
>
w
[
i
]
j>w[i]
j>w[i]时
j
−
w
[
i
]
j-w[i]
j−w[i];
j
<
w
[
i
]
j<w[i]
j<w[i]时
w
[
i
]
−
j
w[i]-j
w[i]−j。
δ
\delta
δ. 去除前面的砝码,放下第
i
i
i个砝码,即他自身的质量:
d
p
[
i
]
[
w
[
i
]
]
=
1
dp[i][w[i]] = 1
dp[i][w[i]]=1 ;
状态转移方程代码片段:
if(dp[i-1][j] == 1){ // 在上一个砝码摆放后能得出的质量基础上。讨论三种情况
dp[i][j] = 1; // 不放砝码。
dp[i][j + W[i]] = 1; // 把第i个砝码放在右边 就能表示出j+a[i]的重量
if(j-W[i]<0){dp[i][W[i] - j] = 1;} // 把第i个砝码放在左边 就能表示出|j-a[i]|的重量
else{ dp[i][j - W[i]] = 1;}
}
(3)、边界初始化
前
i
i
i个砝码一定可以表示出质量
w
[
i
]
w[i]
w[i],即
d
p
[
i
]
[
w
[
z
]
]
=
1
dp[i][w[z]]=1
dp[i][w[z]]=1;
个人题解C语言代码如下:
#include<stdio.h>
int N;
int W[105];
int dp[105][100006];
int main(){
int i,j;
scanf("%d",&N);
for(i=0;i<N;i++){ //输入
scanf("%d",&W[i]);
}
int Wight=W[0];
dp[0][W[0]] = 1; //边界
for(i=1;i<N;i++){ //核心
Wight = Wight+W[i];
for(j=1;j<Wight+1;j++){
if(dp[i-1][j] == 1){
dp[i][j] = 1;
dp[i][j + W[i]] = 1;
if(j-W[i]<0){dp[i][W[i] - j] = 1;}
else{ dp[i][j - W[i]] = 1;}
}
}
dp[i][W[i]] = 1 ;
}
int ans=0;
for(j=1;j<Wight+1;j++){ //统计
if(dp[N-1][j] == 1){
ans++;
}
}
printf("%d",ans);
return 0;
}