Bootstrap

第十二届蓝桥杯省赛 C/C++大学B组 试题G:砝码称重

试题题目
在这里插入图片描述
本题为编程题第二题
解题思路:
方法一:暴力求解
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 (3N1)/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[i1][j] ;
      β \beta β V [ i ] [ k + + ] V[i][k++] V[i][k++] = V [ i − 1 ] [ j ] V[i-1][j] V[i1][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[i1][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][ja[i]]=1
  需要首先判断: 前 i − 1 i-1 i1 个砝码都能否表示出质量 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 i1能表示出质量 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 i1能表示出质量 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 i1能表示出质量 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][jw[i]]=1;注意 j > w [ i ] j>w[i] j>w[i] j − w [ i ] j-w[i] jw[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;
}
;