求大神解答一道C/C++问题

qq_34528718 2016-04-14 08:44:30
题目描述

你N个正整数a[1]...a[N],在最初的时候,你选择一个正整数X,然后以后每一步,你可以使一个数a[i] 变成 a[i] + X,或者 a[i] - X,聪明的你,一定会知道怎么选择这个X,使得最后所有的数都变成相等,而且使用的变化步数最少。

输入要求

多组测试数据。对于每组数据,一个N(2 <= N <= 1000),接下来一行有N个数a[1]...a[N] (1 <= a[i] <= 10^6)。保证这N个数不全相等。

输出要求

每组数据单独一行,你找出的正整数X,以及最少步数,两个数用一个空格隔开.

假如输入

3
1 2 3
4
3 5 7 11

应当输出

1 2
2 5
...全文
258 12 打赏 收藏 转发到动态 举报
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
旧剑鞘 2016-04-17
  • 打赏
  • 举报
回复
引用 10 楼 qq_34528718 的回复:
还是不能通过测试
那几个用例过不了? 在求平均数和距离最近的值时,不要用整型变量,会有误差的,修改完再测测,算法是可以推导出来的。
qq_34528718 2016-04-15
  • 打赏
  • 举报
回复
引用 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; }
qq_34528718 2016-04-15
  • 打赏
  • 举报
回复
还是不能通过测试
chehw_1 2016-04-15
  • 打赏
  • 举报
回复
修改了两处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;
}


chehw_1 2016-04-14
  • 打赏
  • 举报
回复
第一次贴的取平均数这一步是错的,应该先对数组排序并取中间那个数。 至于为什么是中间那个数,我是在用穷举法计算时发现的,但不知道该怎么证明。 以下是测试代码: 其中,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;
}
旧剑鞘 2016-04-14
  • 打赏
  • 举报
回复
引用 6 楼 qq_34528718 的回复:
新数组中离平均数最近的数加上原数组最小数是将来的相等的目的数,这里的平均数是原数组还是新数组的
新数组
qq_34528718 2016-04-14
  • 打赏
  • 举报
回复
新数组中离平均数最近的数加上原数组最小数是将来的相等的目的数,这里的平均数是原数组还是新数组的
醉花阴柳 2016-04-14
  • 打赏
  • 举报
回复
引用 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] 受教了
旧剑鞘 2016-04-14
  • 打赏
  • 举报
回复
引用 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; 一步到位
醉花阴柳 2016-04-14
  • 打赏
  • 举报
回复
我觉得这题和平均数没有关系,比如三个数据1 6 6,很明显X=5只需要1步即可,和平均数无关,和众数有关. 初步思路如下: 找出输入数据的最大值maxn,最小值minn,和众数. 取lim=max{abs(众数-maxn),abs(众数-minn)} (如果众数有多个取最大值) 则X的范围应该在(0,lim],对其中每个值枚举计算所需步数即可.
旧剑鞘 2016-04-14
  • 打赏
  • 举报
回复
引用 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,新数组中离平均数最近的数加上原数组最小数是将来的相等的目的数。然后步数遍历一遍就可以计算了。
chehw_1 2016-04-14
  • 打赏
  • 举报
回复
一种简单的解法(可能不是最优的算法): (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; }

69,371

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧