user-0 提问: 求大神解答一道C/C++问题

回复:
user: user-1
content: 引用 10 楼 qq_34528718 的回复:还是不能通过测试 那几个用例过不了？ 在求平均数和距离最近的值时，不要用整型变量，会有误差的，修改完再测测，算法是可以推导出来的。

user: user-0
content: 引用 7 楼 face_t0_sea 的回复:[quote=引用 6 楼 qq_34528718 的回复:] 新数组中离平均数最近的数加上原数组最小数是将来的相等的目的数，这里的平均数是原数组还是新数组的 新数组[/quote]  答案错误50%  #include<stdio.h> #include<math.h> int num[1001]; int newnum[1001]; int N; void quiksort(int a[],int low,int high) { 	int i=low; 	int j=high; 	int temp=a[i]; 	if(low<high) 	{ 		while(i<j) 		{ 			while((a[j]<=temp)&&(j>i)) 			{ 				j--; 			} 			a[i]=a[j]; 			while((a[i]>=temp)&&(j>i)) 			{ 				i++; 			} 			a[j]=a[i]; 		} 		a[i]=temp; 		quiksort(a,low,i-1); 		quiksort(a,j+1,high); 	} 	else 	{ 		return; 	} } int maxu(int a, int b) { 	int s=0; 	for(s=a;s>=1;s--) 	{ 		if(a%s==0&&b%s==0) 		{ 			break; 		} 	} 	return s; }  int MAXX() { 	    int i,X; 		X=maxu(newnum[1],newnum[2]); 		for(i=3;i<=N;i++) 		{ 			if(newnum[i]!=0) 			{ 			if(X<newnum[i]) 			X=maxu(X,newnum[i]); 			else 			X=maxu(newnum[i],X); 		    } 		} 		return X; }  int simT(int av) { 	    int i,j; 	    int T,min=10000000,mid; 	    for(i=1;i<=N;i++) 		{ 			mid=abs(newnum[i]-av); 			if(mid<min) 			{ 				j=i; 				min=mid; 			} 		} 		T=newnum[j]+num[N]; 		return T; }  int STEP(int T,int X) { 	int i; 	int step=0; 	for(i=1;i<=N;i++) 		{ 			step+=abs(num[i]-T)/X; 		} 		return step; }  int main() { 	int i; 	int X,step; 	int T,sum,av,mid; 	while(scanf("%d",&N)!=EOF) 	{ 		step=sum=0; 		for(i=1;i<=N;i++) 		{ 			scanf("%d",&num[i]); 		} 		quiksort(num,1,N); 		for(i=1;i<=N;i++)  		{ 			newnum[i]=num[i]-num[N]; 			sum+=newnum[i]; 		} 		av=sum/N; 		X=MAXX(); 		T=simT(av); 		mid=STEP(T,X); 		T=simT(av+1); 		step=STEP(T,X); 		if(step>mid) step=mid; 		printf("%d %d\n",X,step);	 	} 	return 0; }

user: user-0
content: 还是不能通过测试

user: user-2
content: 修改了两处bug, 重贴一下：    #include <stdio.h> #include <stdlib.h> #include <math.h> #include <limits.h>  #define swap(m, n) do {int r = m; m = n; n = r;} while(0)   int gcd(int m, int n) { 	int r; 	if(m < n) swap(m, n); 	if(n == 0) return m;  	 	while((r = (m % n))) 	{ 		m = n; 		n = r; 	} 	return n; }  #define MAX_ITEMS (1000) int a[] =  {4, 8, 12, 14, 16, 20, 30, 32, 34, 35, 36};  static inline int calc_min_steps(int dst, int N, int * min_steps, int * X) { 	if(N < 1) exit(1); // invalid parameter 	 	// 计算数组个元素与目标值的偏差 	int i;  	int steps; 	int diffs[MAX_ITEMS]; 	int x = 0; 	 	for(i = 0; i < N; ++i) 	{ 		diffs[i] = abs(a[i] - dst); 		if(x == 0 && diffs[i]) x = diffs[i]; 	} 	// 计算偏差值的最大公约数 x 	//~ x = diffs[0];	 	for(i = 1; i < N; ++i) 	{ 		if(diffs[i]) // 仅计算偏差值不为0的情况 		{ 			x = gcd(x, diffs[i]); 		} 	} 	 	// 计算步数 	if(x == 0) // 只有一个元素的情况，或所有元素均为0的情况，此时不需要执行任何步骤 	{ 		* min_steps = 0; 		* X = x; 		return 0; 	} 	 	steps = 0; 	for(i = 0; i < N; ++i) 	{ 		steps += diffs[i] / x; 	} 	 	* min_steps = steps; 	*X = x; 	return 0; }   void exhaustive_method() { 	int N = (int)(sizeof(a) / sizeof(a[0])); 	 	int dst; 	int steps; 	int x; 	int min = INT_MAX; 	int min_dst = INT_MAX; 	int min_x = INT_MAX; 	printf(" N = %d\n", N); 	//~ return 0; 	for(dst = a[0]; dst <= a[N - 1]; ++dst) 	{ 		steps = 0; 		calc_min_steps(dst, N, &steps, &x); 		 		printf("dst = %d, ", dst); 		if(steps < min)  		{ 			min = steps; 			min_dst = dst; 			min_x = x; 		} 		printf("x = %d: steps = %d\n", x, steps); 	} 	printf("min steps = %d, min_dst = %d, min_x = %d\n", min, min_dst, min_x); 	 }  void fast_method() { 	int N = (int)(sizeof(a) / sizeof(a[0])); 	int mid_pos = (N + 1) / 2 - 1; 	int min_steps = 0; 	int X = 0; 	 	int min_dst = a[mid_pos]; 	 	calc_min_steps(min_dst, N,  &min_steps, &X); 	//~ printf("min steps = %d, min_dst = %d, min_x = %d\n", min_steps, min_dst, X); 	 	if((N & 0x01) == 0) // N 为偶数时有两个中位数，需要多检查一个中位数 	{ 		int steps = INT_MAX; 		int x = INT_MAX; 		int dst = a[mid_pos + 1]; 		calc_min_steps(dst, N,  &steps, &x); 		if(steps < min_steps) 		{ 			min_dst = dst; 			min_steps = steps; 			X = x; 		}		 	} 	printf("N = %d, min_steps = %d, min_dst = %d, min_x = %d\n", N, min_steps, min_dst, X); }   int main(int argc, char **argv) { 	exhaustive_method(); 	 	printf("====  fast_methos ====\n"); 	fast_method(); 	return 0; }

user: user-2
content: 第一次贴的取平均数这一步是错的，应该先对数组排序并取中间那个数。 至于为什么是中间那个数，我是在用穷举法计算时发现的，但不知道该怎么证明。  以下是测试代码： 其中，exhausitive_method是用穷举法，遍历数组最小值和最大值之间的所有数作为目标值（dst)，并获取其中的最小步数； fast_method是直接取中间位置的那个数为目标值（dst），然后计算步数。（如果数组元素个数为偶数，将有两个中间位置，需要计算两次）； 注： 代码中省略了排序算法，假设数组已经排好了序。     #include <stdio.h> #include <stdlib.h> #include <math.h> #include <limits.h>  static inline void swap(int m, int n) { 	int r = m; 	m = n; 	n = r; }  int gcd(int m, int n) { 	int r; 	if(m < n) swap(m, n); 	 	while((r = (m % n))) 	{ 		m = n; 		n = r; 	} 	return n; }  #define MAX_ITEMS (1000) int a[] = {4, 8, 12, 13, 15, 16, 18, 20, 30, 32};  static inline int calc_min_steps(int dst, int N, int * min_steps, int * X) { 	if(N < 1) exit(1); // invalid parameter 	 	// 计算数组个元素与目标值的偏差 	int i;  	int steps; 	int diffs[MAX_ITEMS]; 	int x = 0; 	 	for(i = 0; i < N; ++i) 	{ 		diffs[i] = abs(a[i] - dst); 	} 	// 计算偏差值的最大公约数 x 	x = diffs[0]; 	for(i = 1; i < N; ++i) 	{ 		if(diffs[i]) // 仅计算偏差值不为0的情况 		{ 			x = gcd(x, diffs[i]); 		} 	} 	 	// 计算步数 	if(x == 0) // 只有一个元素的情况，或所有元素均为0的情况，此时不需要执行任何步骤 	{ 		* min_steps = 0; 		* X = x; 		return 0; 	} 	 	steps = 0; 	for(i = 0; i < N; ++i) 	{ 		steps += diffs[i] / x; 	} 	 	* min_steps = steps; 	*X = x; 	return 0; }   void exhaustive_method() { 	int N = (int)(sizeof(a) / sizeof(a[0])); 	 	int dst; 	int steps; 	int x; 	int min = INT_MAX; 	int min_dst = INT_MAX; 	int min_x = INT_MAX; 	printf(" N = %d\n", N); 	//~ return 0; 	for(dst = a[0]; dst <= a[N - 1]; ++dst) 	{ 		steps = 0; 		calc_min_steps(dst, N, &steps, &x); 		 		printf("dst = %d, ", dst); 		if(steps < min)  		{ 			min = steps; 			min_dst = dst; 			min_x = x; 		} 		printf("x = %d: steps = %d\n", x, steps); 	} 	printf("min steps = %d, min_dst = %d, min_x = %d\n", min, min_dst, min_x); 	 }  void fast_method() { 	int N = (int)(sizeof(a) / sizeof(a[0])); 	int mid_pos = (N + 1) / 2 - 1; 	int min_steps = 0; 	int X = 0; 	 	int min_dst = a[mid_pos]; 	 	calc_min_steps(min_dst, N,  &min_steps, &X); 	//~ printf("min steps = %d, min_dst = %d, min_x = %d\n", min_steps, min_dst, X); 	 	if((N & 0x01) == 0) // N 为偶数时有两个中位数，需要多检查一个中位数 	{ 		int steps = INT_MAX; 		int x = INT_MAX; 		int dst = a[mid_pos + 1]; 		calc_min_steps(dst, N,  &steps, &x); 		if(steps < min_steps) 		{ 			min_dst = dst; 			min_steps = steps; 			X = x; 		}		 	} 	printf("N = %d, min_steps = %d, min_dst = %d, min_x = %d\n", N, min_steps, min_dst, X); }   int main(int argc, char **argv) { 	exhaustive_method(); 	 	printf("====  fast_methos ====\n"); 	fast_method(); 	return 0; }

user: user-1
content: 引用 6 楼 qq_34528718 的回复:新数组中离平均数最近的数加上原数组最小数是将来的相等的目的数，这里的平均数是原数组还是新数组的 新数组

user: user-0
content: 新数组中离平均数最近的数加上原数组最小数是将来的相等的目的数，这里的平均数是原数组还是新数组的

user: user-3
content: 引用 2 楼 face_t0_sea 的回复:[quote=引用 1 楼 chehw_1 的回复:] 一种简单的解法（可能不是最优的算法）：  （1）计算所有数的平均数ave; （2）建立一个临时数组，存储每个数与平均数的差值diffs[N]； （3）计算diffs[N]中所有数的最大公约数X； int X = diffs[0]; int i = 1; while(i < N) {     X = gcd(X, diffs[i++]); } （4）计算所需的步数： steps = 0; for(i = 0; i < N; ++i) {     steps += abs(diffs[i]) / X; } 最大 公约数的思想是对的，但是不是和平均数的差；把原数组排序，然后所有数减去最小数之后得到新数组，再求最大公约数即为X，新数组中离平均数最近的数加上原数组最小数是将来的相等的目的数。然后步数遍历一遍就可以计算了。[/quote] 受教了

user: user-1
content: 引用 3 楼 qq_29120981 的回复:我觉得这题和平均数没有关系,比如三个数据1 6 6,很明显X=5只需要1步即可,和平均数无关,和众数有关.  初步思路如下: 找出输入数据的最大值maxn,最小值minn,和众数. 取lim=max{abs(众数-maxn),abs(众数-minn)} (如果众数有多个取最大值) 则X的范围应该在(0,lim],对其中每个值枚举计算所需步数即可.  1  6  6   第一步减去最小数： 0   5    5 最大公约数，除去0是5； 一步到位

user: user-3
content: 我觉得这题和平均数没有关系,比如三个数据1 6 6,很明显X=5只需要1步即可,和平均数无关,和众数有关.  初步思路如下: 找出输入数据的最大值maxn,最小值minn,和众数. 取lim=max{abs(众数-maxn),abs(众数-minn)} (如果众数有多个取最大值) 则X的范围应该在(0,lim],对其中每个值枚举计算所需步数即可.

user: user-1
content: 引用 1 楼 chehw_1 的回复:一种简单的解法（可能不是最优的算法）：  （1）计算所有数的平均数ave; （2）建立一个临时数组，存储每个数与平均数的差值diffs[N]； （3）计算diffs[N]中所有数的最大公约数X； int X = diffs[0]; int i = 1; while(i < N) {     X = gcd(X, diffs[i++]); } （4）计算所需的步数： steps = 0; for(i = 0; i < N; ++i) {     steps += abs(diffs[i]) / X; } 最大 公约数的思想是对的，但是不是和平均数的差；把原数组排序，然后所有数减去最小数之后得到新数组，再求最大公约数即为X，新数组中离平均数最近的数加上原数组最小数是将来的相等的目的数。然后步数遍历一遍就可以计算了。

user: user-2
content: 一种简单的解法（可能不是最优的算法）：  （1）计算所有数的平均数ave; （2）建立一个临时数组，存储每个数与平均数的差值diffs[N]； （3）计算diffs[N]中所有数的最大公约数X； int X = diffs[0]; int i = 1; while(i < N) {     X = gcd(X, diffs[i++]); } （4）计算所需的步数： steps = 0; for(i = 0; i < N; ++i) {     steps += abs(diffs[i]) / X; }