1、已知两个数字为1~30之间的数字,甲知道两数之和,乙知道两数之积,甲问乙:“你知道是哪两个数吗?”乙说:“不知道”。乙问甲:“你知道是哪两个数吗?”甲说:“也不知道”。于是,乙说:“那我知道了”,随后甲也说:“那我也知道了”,这两个数是什么?
答:1和4 或者1和7
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]; }
硬搜的话肯定是可以出结果的,但时间复杂度太高。
第一种方法:
设 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]; }
#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]; }