D-1 : 在一个火车站:每 10 分钟就有一火车离站向南开去;每 10 分钟,也有另外一辆火车离站向北开去。每天,你到达这个火车站的时间并不是固定的(换言之,在时间上你是随机到达火车站的)。但是在你每次到达以后,你就会乘坐最先到站的火车离开,而不管它是往北或者是往南开。这样在乘坐了一年以后,你发现在 90% 的天数里,你所乘坐的是南行的火车。请问这是为什么?
解析:答案是“南行的火车时间比北行的时间早 1 分钟”。这是一道概率题。我们考虑连续两次向南发车时间点 a 、 b 之间的时间段,在这个时段内肯定会在某个时刻向北发一次车,该时间点我们记为 c 。考虑到每次到火车站后会选择最先到站的火车离开,那么在 a 、 b 间的时间段内,选择向北火车的概率是 (c-a)/(a-b)*100% 。当我们把目光从 a 、 b 间的时间段扩展到整一天时,该结论同样正确。所以,当你发现 90% 的天数中乘坐的是南行的火车时,即有 (c-a)/(10min)*100%=(1-90%) 成立,易得“南行的火车时间比北行的时间早 1 分钟”。
D-2 :有两个数组 a,b ,大小都为 n, 数组元素的值任意,无序。要求通过交换 a,b 中的元素,使数组 a 元素的和与数组 b 元素的和之间的差最小。
解析:这是华为的一道面试题,要求在 8 分钟内解决。一种简单可行的方案是遍历查询(也就是穷举法),对于 A 、 B 数组中的数据存放在一个数组 Data 中,假设两个数组各有 NUM 个元素。那么我们在 Data 数组中选择有 NUM 个元素的所有情况(使用组合代数),计算选出子数组的和与未选数据的和之间的差值。记录最小差值的情况,然后显示之。
当然,这种方案显然不是最优解,但是确实有个可行的方案。(其实,我认为即使使用该方案在 8 分钟内实现 C 代码也是不现实的,除非是伪代码)。 Chinaunix 论坛上 有一帖子 专门讨论它,有兴趣的朋友可以去看看。下面给出我写的实现代码
- #include <stdio.h>
- #include <stdlib.h>
- #define NUM 10
- int A[NUM] = {11, 20, -3, 123, 18, 19, 171, 19, 123, 10};
- int B[NUM] = {1, 23, 19, 41, 39, 123, 190, 238, 179, 25};
- int Data[NUM*2]; // 包含了数组A和B中的所有元素
- int Data_inc[NUM]; // 保存了从Data数组中选出的NUM个元素
- int Data_temp[NUM]; // 记录了当前能产生差值最小情况的NUM个元素
- void Print(int *a, int n){
- int i;
- for(i=0;i<n;++i)
- printf("%d ",a[i]);
- printf("/n");
- }
- int get_sum(int *a, int n){
- int sum,i;
- for(sum=0,i=0;i<n;i++)
- sum += a[i];
- return(sum);
- }
- static int diff = 0; // 保存了当前的最小差值
- static int counter = 0; // 记录穷举的次数
- void Handle(int sum){
- int sum0, sum1, i;
- // 寻找最优情况,并记录之
- sum0 = get_sum(Data_inc, NUM);
- sum1 = sum - sum0;
- if(counter==0 || diff > abs(sum0-sum1)){
- diff = abs(sum0-sum1);
- for(i=0;i<NUM;i++)
- Data_temp[i] = Data_inc[i];
- }
- counter++;
- }
- void Com(int n, int m, int sum){
- int i; // 使用组合算法从Data数组中选出NUM个元素保存在Data_inc数组中
- for(i=n;i>=m;--i){ // 抽屉子算法
- Data_inc[m]=Data[i];
- if(m>0)
- Com(i-1,m-1, sum);
- else
- Handle(sum);
- }
- }
- int main()
- {
- int i;
- for(i=0;i<NUM*2;i++)
- if(i<NUM)
- Data[i] = A[i];
- else
- Data[i] = B[i-NUM];
- printf("Sum in A: %d, Sum in B: %d/n", get_sum(A, NUM), get_sum(B, NUM));
- Print(Data, NUM*2);
- Com(NUM*2-1,NUM-1, get_sum(Data, NUM*2));
- Print(Data_temp, NUM);
- printf("Diff Value is %d, Count %d. /n", diff, counter);
- return 0;
- }
E-2 :将 1,2,3,4,5,6,7,8,9 共 9 个数分成三组 , 组成 3 个三位数 , 且使这 3 个三位数构成 1:2:3 的比例 , 例如 :3 个三位数 192,384,576 满足以上条件 .192:384:576=1:2:3 。试求出所有满足条件的 3 个三位数。
解析: 1 ~ 9 组成的最小三位数是 123 ,最大的是 987 ,由于要满足 1 : 2 : 3 的关系,最小的那个数应该不到于 987/3 = 329 。这个问题的求解可以对每个数 n(123<=n<=329) 使用枚举法。
对于每种情况,鉴于只是需要判断这三个数是否由 1~9 组成且各个数组不相同,即这三个数的各位数是否覆盖了1~9 ,那么一种合理的解法是判断各位数字的积是否等于 9 !且各位数字的和是否等于 45 。
在我自己写的程序中,使用的方法是对三个数的每位上的数做简单加法,平方和以及立方和;继而与 45 、 285 和2025 比较。实现如下:
- #include <stdio.h>
- int judge2(int a, int b, int c){ // 满足返回0,否则返回1
- int x[3] = {a, b, c};
- int i, value0, value;
- int sum, sum2, sum3;
- sum = sum2 = sum3 = 0;
- for(i=0;i<3;i++){
- value0 = x[i];
- do{
- value = value0 % 10;
- sum += value;
- sum2 += value * value;
- sum3 += value * value * value;
- }while(value0 = value0 / 10);
- }
- if(sum==45 && sum2==285 && sum3==2025)
- return 0;
- return 1;
- }
- int main()
- {
- int a,b,c;
- for(a=123;a*3<987;a++){
- b = a*2;
- c = a*3;
- if(!judge2(a, b, c))
- printf(" %d : %d : %d/n", a, b, c);
- }
- return 0;
- }