2012Google校园招聘笔试题

时间:2021-03-16 18:49:55

1、已知两个数字为1~30之间的数字,甲知道两数之和,乙知道两数之积,甲问乙:“你知道是哪两个数吗?”乙说:“不知道”。乙问甲:“你知道是哪两个数吗?”甲说:“也不知道”。于是,乙说:“那我知道了”,随后甲也说:“那我也知道了”,这两个数是什么?

答案: 
允许两数重复的情况下
答案为x
=1, y=4; 甲知道和A=x+y=5, 乙知道积B=x*y=4
不允许两数重复的情况下有两种答案
答案1: 为x
=1, y=6; 甲知道和A=x+y=7, 乙知道积B=x*y=6
答案2: 为x
=1, y=8; 甲知道和A=x+y=9, 乙知道积B=x*y=8

解:
设这两个数为x,y.
甲知道两数之和 A
=x+y;
乙知道两数之积 B
=x*y;

该题分两种情况
允许重复, 有(
1 <= x <= y <= 30);
不允许重复,有(
1 <= x < y <= 30);

当不允许重复, 即(
1 <= x < y <= 30);

1)由题设条件:乙不知道答案
<=> B=x*y 解不唯一
=> B=x*y 为非质数

又∵ x ≠ y
∴ B ≠ k
*k (其中k∈N)

结论(推论1):
B
=x*y 非质数且 B ≠ k*k (其中k∈N)
即:B ∈(
6,8,10,12,14,15,18,20...)
证明过程略

2)由题设条件:甲不知道答案
<=> A=x+y 解不唯一
=> A >= 5;

分两种情况
A
=5,A=6时x,y有双解
A
>=7 时x,y有三重及三重以上解

假设 A
=x+y=5
则有双解
x1
=1,y1=4;
x2
=2,y2=3
代入公式B
=x*y:
B1
=x1*y1=1*4=4; (不满足推论1,舍去)
B2
=x2*y2=2*3=6;
得到唯一解x
=2,y=3 即甲知道答案
与题设条件:“甲不知道答案”相矛盾
故假设不成立, A
=x+y≠5

假设 A
=x+y=6
则有双解
x1
=1,y1=5;
x2
=2,y2=4
代入公式B
=x*y:
B1
=x1*y1=1*5=5; (不满足推论1,舍去)
B2
=x2*y2=2*4=8;
得到唯一解x
=2,y=4
即甲知道答案
与题设条件:“甲不知道答案”相矛盾
故假设不成立, A
=x+y≠6

当A
>=7时
∵ x,y的解至少存在两种满足推论1的解
B1
=x1*y1=2*(A-2)
B2
=x2*y2=3*(A-3)
∴ 符合条件

结论(推论2):A
>= 7

3)由题设条件:乙说“那我知道了”
=> 乙通过已知条件B=x*y及推论(1)(2)可以得出唯一解
即:

A
=x+y, A >= 7
B
=x*y, B ∈(6,8,10,12,14,15,16,18,20...)
1 <= x < y <= 30
x,y存在唯一解

当 B
=6 时:有两组解
x1
=1, y1=6
x2
=2, y2=3 (∵ x2+y2=2+3=5 < 7 ∴不合题意,舍去)
得到唯一解 x
=1, y=6

当 B
=8 时:有两组解
x1
=1, y1=8
x2
=2, y2=4 (∵ x2+y2=2+4=6 < 7 ∴不合题意,舍去)
得到唯一解 x
=1, y=8

当 B
>8 时:容易证明均为多重解

结论:
当B
=6时有唯一解 x=1, y=6 当B=8时有唯一解 x=1, y=8

4)由题设条件:甲说“那我也知道了”
=> 甲通过已知条件A=x+y及推论(3)可以得出唯一解

综上所述,原题所求有两组解:
x1
=1, y1=6
x2
=1, y2=8

当x
<=y时,有(1 <= x <= y <= 30);
同理可得唯一解 x
=1, y=4

2、一个环形公路,上面有N个站点,A1, ..., AN,其中Ai和Ai+1之间的距离为Di,AN和A1之间的距离为D0。
高效的求第i和第j个站点之间的距离,空间复杂度不超过O(N)
它给出了部分代码如下:

#define N 25
double D[N]
....
void Preprocess()
{
//Write your code1;
}
double Distance(int i, int j)
{
//Write your code2;
}
const int N = 10;  
int D[N];

int A1toX[N];

void Preprocess()
{
srand(time(
0));

for (int i = 0; i < N; ++i)
{
D[i]
= (rand()/(RAND_MAX+1.0)) * N;
}

A1toX[
1] = D[1]; //from A1 to A2
for (int i = 2; i < N; ++i)
{
A1toX[i]
= A1toX[i-1] + D[i]; //distance from A1 to each point
}
A1toX[
0] = A1toX[N-1] + D[0]; // total length
}

int distance(int i, int j)
{
int di = (i == 0) ? 0 : A1toX[i-1];
int dj = (j ==0) ? 0 : A1toX[j-1];
int dist = abs(di - dj);
return dist > A1toX[0]/2 ? A1toX[0] - dist : dist;
}

int main(void)
{
Preprocess();
for (int i = 0; i <N; ++i)
{
cout
<<D[i]<<" ";
}
cout
<<endl;
for (int i = 1; i <= N; ++i)
{
cout
<<"distance from A1 to A"<<i<<": "<<distance(1, i)<<endl;
}
return 0;
}

3、 一个字符串,压缩其中的连续空格为1个后,对其中的每个字串逆序打印出来。比如"abc   efg  hij"打印为"cba gfe jih"。

#include<iostream>  
#include
<cstdio>
#include
<stack>
#include
<string>
using namespace std;

string reverse(string str)
{
stack
<char> stk;
int len = str.length();
string ret = "";

for (int p = 0, q = 0;p < len;)
{
if (str[p] == ' ')
{
ret.append(
1,' ');
for (q = p; q < len && str[q] == ' '; q++)
{}
p
= q;
}
else
{
for (q = p; q < len && str[q] != ' '; q++)
{
stk.push(str[q]);
}
while(!stk.empty())
{
ret.append(
1,stk.top());
stk.pop();
}
p
= q;
}
}
return ret;
}
int main(void)
{
string s = "abc def ghi";
cout
<<reverse(s).c_str()<<endl;
return 0;
}

4、将一个较大的钱,不超过1000000(10^6)的人民币,兑换成数量不限的100、50、10、5、2、1的组合,请问共有多少种组合呢?(完全背包)(其它选择题考的是有关:操作系统、树、概率题、最大生成树有关的题。)。

第一种方法(母函数):

 

#define NUM 7  
int money[NUM] = {1, 2, 5, 10, 20, 50, 100};

// 母函数解法
int NumOfCoins(int value)
{
int i , j , k , c1[1010] , c2[1010];
for(i = 0 ; i <= value ; ++i)
{
c1[i]
= 1;
c2[i]
= 0;
}
//第一层循环是一共有 n 个小括号,而刚才已经算过一个了
// i 就是代表的母函数中第几个大括号中的表达式
for(i = 1 ; i < NUM ; ++i)
{
for(j = 0 ; j <= value ; ++j) //j 就是指的已经计算出的各项的系数
{
for(k = 0 ; k+j <= value ; k += money[i]) //k 就是指将要计算的那个括号中的项
c2[k+j] += c1[j];
}
for(j = 0 ; j <= value ; ++j) // 刷新一下数据,继续下一次计算,就是下一个括号里面的每一项
{
c1[j]
= c2[j];
c2[j]
= 0;
}
}
return c1[value];
}

第二种方法(动态规划):
我们可以将它形式化为:

 2012Google校园招聘笔试题

硬搜的话肯定是可以出结果的,但时间复杂度太高。
第一种方法:
设 F[n] 为用那么多种面值组成 n 的方法个数。则 F[n] 可以分成这样互不重复的几个部分:
只用 50 及以下的面值构成 [n] + 0 张 100
只用 50 及以下的面值构成 [n-100] + 1 张 100
只用 50 及以下的面值构成 [n-200] + 2 张 100
……
也就是说,按 F[n] 的组成方案中 100 的张数,将 F[n] 划分成若干等价类,等价类的划分要不重复、不遗漏。这些等价类恰好完整覆盖了 F[n] 的所有情况。
然后对于 50 及以下的方案又可以按 50 的张数划分等价类。于是像这样一层一层递归下去……就可以得到结果了。
把上面的递归过程反过来,从下往上递推,这就是动态规划了。代码(用到了一些 C99 特性,比如栈上的可变长数组):
时间复杂度 < O(N^2)

#define NUM 7  
int money[NUM] = {1, 2, 5, 10, 20, 50, 100};
// 动态规划解法
int NumOfCoins(int value)
{
int i , j , t , dp[7][1010];
for(i = 0 ; i <= value ; ++i)
dp[
0][i] = 1;

for(i = 1 ; i < NUM ; ++i)
{
for(j = 0 ; j <= value ; ++j)
{
t
= j;
dp[i][j]
= 0;
while(t >= 0)
{
dp[i][j]
+= dp[i-1][t];
t
-= money[i];
}
}
}
return dp[6][value];
}

其中 dp[i][j] 表示只用第 i 张面值及以下构成 j 用多少种方法。
改进如下:
a[6][n] = ar[6][n-100]     // 至少包含 1 张 100 的拆分个数
              + ar[5][n]         // 不包含 100 的拆分个数

直接把时间复杂度从 O(n^2) 降到了 O(n):

#define NUM 7  
int money[NUM] = {1, 2, 5, 10, 20, 50, 100};
// 动态规划解法(完全背包)
int NumOfCoins(int value)
{
int i , j , dp[7][1010];
for(i = 0 ; i <= value ; ++i)
dp[
0][i] = 1;

for(i = 1 ; i < NUM ; ++i)
{
for(j = 0 ; j <= value ; ++j)
{
if(j >= money[i])
dp[i][j]
= dp[i][j - money[i]] + dp[i - 1][j];
else
dp[i][j]
= dp[i-1][j];
}
}
return dp[6][value];
}

或者使用滚动数组也是可以的

#define NUM 7    
int money[NUM] = {1, 2, 5, 10, 20, 50, 100};
int f[1010] , bf[1010];
// f[j] == f[i][j] bf[j] == bf[i-1][j]
int NumofCoin2(int value)
{
int i , j;
for(j = 0 ; j <= value ; ++j)
f[j]
= 0 , bf[j] = 0;
bf[
0] = 1;

for(i = 0 ; i < NUM ; ++i)
{
for(j = 0 ; j <= value ; ++j)
{
if(j >= money[i])
f[j]
= f[j-money[i]] + bf[j];
else
f[j]
= bf[j] ;
}

for(j = 0; j <= value ; ++j)
bf[j]
= f[j] , f[j] = 0;
}
return bf[value];

}