现在的位置: 首页 > 综合 > 正文

面试题目搜集(6)

2014年07月08日 ⁄ 综合 ⁄ 共 6138字 ⁄ 字号 评论关闭

(1)面试题目搜集1

(2)面试题目搜集2

(3)面试题目搜集3

(4)面试题目搜集4

(5)面试题目搜集5

(6)面试题目搜集6

(7)百度面试题目搜集


1.一个台阶总共有n级,如果一次可以跳1级,也可以跳2级,求总共有多少种跳法。

相信大家一看到这个题就知道是个斐波拉契序列,没错,它就是这个答案。

f[n] = f[n-1] + f[n-2];//f[n-1]表示f[n]跳一级剩下的级数,f[n-2]表示f[n]跳二级剩下的级数

f[1] = 1;

f[2] = 2;//1+1或者2

int Fibonacci(int n)
{
	if(n == 1)return 1;
	if(n == 2)return 2;
	return Fibonacci(n-1)+Fibonacci(n-2);
}

      <<编程之美>>上关于斐波拉契序列的求法给出了四种答案:(1)递归;(2)迭代;(3)直接推了一个方程;(4)通过[f(n) f(n-1)] = [f(n-1) f(n-2)] A,转换为A矩阵的n次方,n表示成2进制,计算出A的幂。(海涛的想法就是A^n=(A^n/2)^2,这样降到o(logn)的时间复杂度。)

     我看到这个题想了这么一个问题:”假设我要将一个数转换成1和2的和,1和2可以任意多个,求有多少种做法?”那是不是也可以同样的一个表达式来递推:f[n] = f[n-1] + f[n-2]。后来仔细想想发现不是,f[n] = f[n-1] + f[n-2]这个表达式隐藏了一个顺序的概念。什么意思?举个例:假设有3个台阶,则1+2的跳法和2+1的跳法是不一样的跳法;但是我们如果将3转换成1和2的和时,1+2和2+1其实是一种解。

     这就说明我们不能用这个解法来解“假设我要将一个数转换成1和2的和”的问题。那这个递推关系式是什么,考虑了一下应该是这样的。我们只要强制转换一下这个问题,怎么转换呢?我们只要限定我们一定是先跳1级台阶,才能再跳2级台阶,就是说当你开始跳2级台阶了以后,就不能再跳1级台阶了,这样解就不会出现1+2和2+1这种重复解了。那么按照这个思想我们就可以写出如下递推关系式:f[i,n]
= f[i-1,n] + f[i-1,n-2] + f[i-1,n-4]...一直到n-2*k>0,此时i只能为2和1。按照上面这个思想我们可以写出这个代码:

#include <iostream>
#include <vector>
using namespace std;

#define NUM 2  
int steps[NUM]={1,2};

int CalPermNum(int index,int num)
{
	if(index < 0 || num <0 ) return 0;
	if(index == 0 || num == 1)return 1;

	int sum = 0;

	for(int i = num; i >= 0; i-= steps[index]){
		sum += CalPermNum(index-1,i);//一定是index-1
	}

	return sum;
}

void main()  
{  
	vector<int> vec;  
	cout<<CalPermNum(1,5)<<endl;
	system("pause");
} 


2. 再举个例子:将一个较大的钱,不超过1000000(10^6)的人民币,兑换成数量不限的100、50、10、20、5、2、1的组合,请问共有多少种组合呢?

   按照上面的分析我们不能简单的将F(N)=F(N-100)+F(N-50)+F(N-20)+F(N-10)+F(N-5)+F(N-2)+F(N-1),因为这里面的大部分解都是重复的。

 
  同样,我们需要额外加一个变量,表示当前用到了第index中人民币的面值。

(1)动态规划的递归解法

    我们这里设一个数组表示人民币的面值:int money[MAX]={1,2,5,10,20,50,100};此时,我们就可以按照上面的思路来解:

(1)只用 50 及以下的面值构成 [n] + 0 张 100
(2)只用 50 及以下的面值构成 [n-100] + 1 张 100
(3)只用 50 及以下的面值构成 [n-200] + 2 张 100
……
   也就是说,按 F[n] 的组成方案中 100 的张数,将 F[n] 划分成若干等价类,等价类的划分要不重复、不遗漏。这些等价类恰好完整覆盖了 F[n] 的所有情况。

#include <iostream>
#include <vector>
using namespace std;

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

int CalPermNum(int index,int num)
{
	if(index < 0 || num <0 ) return 0;
	if(index == 0 || num == 1)return 1;

	int sum = 0;

	for(int i = num; i >= 0; i-= money[index]){
		sum += CalPermNum(index-1,i);//一定是index-1
	}

	return sum;
}

void main()  
{  
	vector<int> vec;  
	cout<<CalPermNum(1,5)<<endl;
	system("pause");
} 

(2)   完全背包递归解法

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

我们将当前的解分为用了第i张的面值和没有第i张的面值利用这种思想,我们可以用递归写出如下代码。

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

int CalPermNum(int index,int num)
{
	if(index < 0 || num <0 ) return 0;
	if(index == 0 || num == 1)return 1;
	//一定index-1和index的混合,细细的体味一下这个意思
	return CalPermNum(index-1,num)+CalPermNum(index,num-money[index]);
}

     这道题其实是一个完全背包问题:http://blog.csdn.net/lsjseu/article/details/11400201给出了四种解法:(1)母函数;(2)动态规划;(3)完全背包问题;(4)完全背包问题滚动数组优化。本文的解法1其实里面的动态规划解法的递归解法,解法2其实就是里面的完全背包递归解法

3. 再提个问题:将一个较大的钱,不超过1000000(10^6)的人民币,兑换成数量不限的100、50、10、5、2、1的组合,将这些组合输出来。

#include <iostream>
#include <vector>
using namespace std;

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

void dfs(int sum,vector<int>& vec, int curSum, int id)  
{  
	int i;  
	if(curSum == sum)  
	{  
		static int inum = 1 ;  
		cout<<"方案 "<<inum++<<": ";  
		for(i = 0; i < vec.size() ; ++i)  
			cout<<vec[i]<<" ";  
		cout<<endl;  
		return;  
	}  

	for(i = id ; i <= sum; ++i)  
	{  
		if(curSum+money[i] <= sum)  
		{  
			vec.push_back(money[i]);  //求组合的时候,下面就是i+1
			dfs(sum,vec,curSum+money[i],i);  //这里不是i+1,表示可以重复
			vec.pop_back();  
		}  
		else  
			break;  
	}  
}  

void main()  
{  
	vector<int> vec;  
	dfs(20,vec,0,0);  
	system("pause");
}  

另外本博客《面试题目搜集2》的第11题整数拆分问题,其实就是同样一个问题。http://blog.csdn.net/lsjseu/article/details/9390443

4. 有一百个灯泡关着的,标号为1到100,现在有100个人,每个人走过去将标号为其倍数的灯泡关掉,最后亮着的有哪些。

分析:其实,就是一个打印出1到100内的完全平方数。

平方数的方法可以这么来求:(n+1)^2 = (n-1)^2 + (2n+1) = 1 + 3 +....+(2n+1)。

即一个n^2 = 1 + 3 +....+(2n-1)。 举例验证:4^2 = 1 + 3 + 5 +(2*4-1)。于是可写出如下o(根号n)代码:

void PrintSquareNum(int num)
{	
	int i = 1;
	int square = 0;
	while(square < num){
		square += i;
		i += 2;
		cout<<square<<" ";
	}
}

5.写一个函数,检查字符是否是整数,如果是,返回整数值。(用4行代码)

思路:和高手学习一下,递归思路

正数: 例如65535 = 6553 *10 +5 ; 6553 = 655*10 +3 。。。。如此递归下去

复数: -65535 = -6553 *10 -5;   -6553 = -655*10 -3 。。。。 但是到了 -6的时候 仍然执行 -6 *10,所以到str[0] = '-'时, 返回 -0.1作为平衡。

long strtoint(char *str,int length){  
	if(length > 1) {  
		return str[0]=='-' ? strtoint(str, length-1)*10-(str[length-1]-'0') : strtoint(str, length-1)*10+str[length-1]-'0';  
	} else {  
		return str[0]=='-' ? -1/10 : str[0]-'0';  
	}  
}  



6.有两个序列a,b,大小都为n,序列元素的值任意整数,无序;
要求:通过交换a,b中的元素,使[序列a元素的和]与[序列b元素的和]之间的差最小。
例如:   
var a=[100,99,98,1,2, 3];

var b=[1, 2, 3, 4,5,40];


求解思路:http://blog.csdn.net/chdhust/article/details/11610459

   当前数组a和数组b的和之差为
    A = sum(a) - sum(b)

    a的第i个元素和b的第j个元素交换后,a和b的和之差为
    A' = sum(a) - a[i] + b[j] - (sum(b) - b[j] + a[i])
           = sum(a) - sum(b) - 2 (a[i] - b[j])
           = A - 2 (a[i] - b[j]) = A - 2x
    设x = a[i] - b[j]

要使交换之后的差|A'|<|A|,则必须满足,x与A同号且0<X<A。如果没有满足条件的话则循环结束,程序结束。

#include <iostream>  
#include <cctype>
#include <cassert>
using namespace std;  

int Sum(int arr[],int len)
{
	assert(arr !=NULL && len>0);

	int sum = 0;

	for(int i=0; i<len; ++i){
		sum += arr[i];
	}

	return sum;
}

void Swap(int &a,int &b)
{
	a = a + b;
	b = a - b;
	a = a - b; 
}

void SwapArrNum(int arrA[],int arrB[],int len)
{
	assert(arrA!=NULL && arrB!=NULL && len>0);

	bool flag;	
	int sumA = Sum(arrA,len);
	int sumB = Sum(arrB,len);

	if(sumB == sumA)return;

	if(sumB > sumA){
		int *temp = arrA;
		arrA = arrB;
		arrB = temp;
	}

	while(true){
		cout<<"Iterator"<<endl;
		flag = false;
		for(int i=0; i<len; i++){
			for(int j=0; j<len; j++){
				int diff = arrA[i] - arrB[j];

				int addA = Sum(arrA,len);
				int addB = Sum(arrB,len);

				if(diff>0 && diff<(addA-addB)){
					swap(arrA[i],arrB[j]);
					flag = true;
				}
			}
		}
		if(!flag)break;
	}
}

void PrintArray(int arr[],int len)
{
	assert(arr!=NULL && len>0);

	for(int i=0; i<len; ++i){
		cout<<arr[i]<<" ";
	}
	cout<<endl;
}

int main()  
{  
	const int MAX = 6;
	int arrA[MAX] = {1,9,8,1,2, 3};
	int arrB[MAX] ={ 1, 2, 3, 4,5,40};

	cout<<Sum(arrA,MAX)<<endl;
	cout<<Sum(arrB,MAX)<<endl;

	SwapArrNum(arrA,arrB,MAX);

	PrintArray(arrA,MAX);
	PrintArray(arrB,MAX);

	cout<<Sum(arrA,MAX)<<endl;
	cout<<Sum(arrB,MAX)<<endl;

	system("pause");  
	return 0;  
}

7.给定N对括号,输出其所有的合法的组合状态,例如,N=3,所有的合法状态为:"((()))”, “(()())”, “(())()”, “()(())”, “()()()”

思路:还是深搜DFS的思路,深搜的过程关键在于记录已经用掉的左括号个数和右括号的个数,当用过的左括号个数小于右括号则非法;当二者个数和大于2N则非法;当二者个数相等且数目等于2N则为合法。传说这是卡特兰数

题目引自:http://www.ahathinking.com/archives/186.html

#include<iostream>
using namespace std;
 
#define PAIR 50
 
char str[PAIR * 2 + 1]; // 设括号对数不超过50, str记录括号组合状态
 
void DFS_bracket(int n, int left_used, int right_used)
{
    if(left_used == right_used && left_used + right_used == 2*n)
    {
        printf("%s\n",str);
        return;
    }
    if(left_used < right_used || left_used + right_used >= 2*n)
    {
        return ;
    }
    int index = left_used + right_used;
    str[index] = '(';
    DFS_bracket(n, left_used + 1, right_used);
 
    str[index] = ')';
    DFS_bracket(n, left_used, right_used + 1);
}
 
void main()
{
    int N;
    scanf("%d", &N);
    DFS_bracket(N,0,0);
}

抱歉!评论已关闭.