笔试面试题记录-
1.猴子分桃
问题:
五只猴子分桃。半夜,第一只猴子先起来,它把桃分成了相等的五堆,多出一只。于是,它吃掉了一个,拿走了一堆; 第二只猴子起来一看,只有四堆桃。于是把四堆合在一起,分成相等的五堆,又多出一个。于是,它也吃掉了一个,拿走了一堆;......其他几只猴子也都是 这样分的。问:这堆桃至少有多少个?
思路:
问题得从最后那个猴子看起,最后那个猴子依然能将桃子分5堆剩一个,则设在最后那个猴子之前剩下数量是A,A%5=1并且还能平分成4堆没有剩余,所以A%4=0。然后,倒数第二只猴子之前的数量就是A+A/4+1个,并且重复之前的步骤,如果在到第一只猴子之前都没遇到问题,那么就能得出结果了。如果在中间出现问题则全部回退到最后那只猴子,重新计算数量。
#include <iostream> #include <stdio.h> using namespace std; void main() { int pick_no = 0;//当前挑选次数 int pick_times = 5;//挑选总数,即有几只猴子 int x = 0; //初始桃子数量 int growing = 0;//每次增幅 while (pick_no < pick_times) { growing = x / (pick_times - 1) + 1; if (x % pick_times == 1 && x % (pick_times - 1) == 0) { x += growing; pick_no++; } else { x++; } } printf("这堆桃至少有%d个!\n", x); }
2.输入一个字符串,输出所有可能的出栈顺序。
#include <iostream>
#include <cstring>
#define M 100
using namespace std;
void stack_in_out(char str[], int len);
void func(char str[], int pstr, int len, char st[], int pst, char temp[], int pt);
int main()
{
char str[M];
cin >> str;
stack_in_out(str, strlen(str));
system("pause");
return 0;
}
void stack_in_out(char str[], int len)
{
char st[M];
int pst = 0;
char temp[M];
int pt = 0;
func(str, 0, len, st, 0, temp, 0);
}
void func(char str[], int pstr, int len, char st[], int pst, char temp[], int pt)
{
if (pt == len)
{
temp[pt] = 0;
puts(temp);
return;
}
if (pstr<len)
{
st[pst] = str[pstr];
func(str, pstr + 1, len, st, pst + 1, temp, pt);
}
if (pst>0)
{
//此处必须保存栈顶,因为后边出栈的操作会影响栈的内容,从而影响后续程序的执行结果
char ch = st[pst - 1];;
temp[pt] = st[pst - 1];
func(str, pstr, len, st, pst - 1, temp, pt + 1);
st[pst - 1] = ch;
}
}
代码2:
#include <iostream> #include <stack> using namespace std; void outprint(stack<int> q){ int count = 0; while (q.size() != 0) { cout << q.top()<<" "; q.pop(); } cout << endl; count++; return; } //q 存放入栈序列 //stk 用于模拟入栈过程 //output 用于存放可能的出栈序列 void allPopSeq(stack<int> q, stack<int> stk, stack<int> output){ if ((q.size() == 0) && (stk.size() == 0) && (output.size() == 5)) { outprint(output); return; } if (q.size() != 0){//入栈 int v = q.top(); stk.push(v); q.pop(); allPopSeq(q, stk, output); stk.pop(); q.push(v);//回溯恢复 } if (stk.size() != 0) //出栈 { int v = stk.top(); stk.pop(); output.push(v); allPopSeq(q, stk, output); output.pop(); stk.push(v);//回溯恢复 } return; } int main(int argc, char** argv){ int count = 0; int arr[5] = { 1, 2, 3, 4, 5 }; stack<int> stkValues; stack<int> stkOutput; stack<int> tmp; int i; for (i = 0; i != 5; ++i){ stkValues.push(arr[i]); } allPopSeq(stkValues, tmp, stkOutput); cout << count << endl; system("pause"); return 0; }
3.一个正整数可以表示为多个正整数相加的格式 如 5=1+1+1+1+1,=1+1+1+2,=1+2+2 , =1+1+3 =2+3 =1+4,写一个函数求有多少种分拆?
参考博客:http://blog.csdn.net/VisDate/article/details/50939202
1 #include<iostream> 2 using namespace std; 3 int s[100];//拆分结果保存在这个数组里 4 int top;//记录个数 5 int total, n;//累加数和所求数 6 int k; 7 void dfs(int index) 8 { 9 int i; 10 if (total == n)//满足打印出来 11 { 12 printf("%d=", n); 13 for (i = 0; i<top - 1; i++) 14 printf("%d+", s[i]); 15 k++; 16 if (k == 4 || top == 1)//四个或最后一个了一换行,换行时的特殊讨论 17 { 18 k = 0; 19 printf("%d\n", s[top - 1]); 20 } 21 else 22 printf("%d;", s[top - 1]); 23 return; 24 } 25 if (total>n) //累加大于所需跳出 26 return; 27 for (i = index; i <= n; i++) 28 { 29 total += i; 30 s[top++] = i; //将每次加的数保存在数组里 31 dfs(i); 32 total -= i; 33 s[--top]; //回退 34 } 35 } 36 int main() 37 { 38 while (cin >> n) 39 { 40 k = 0; 41 top = 0; 42 total = 0; 43 dfs(1); 44 } 45 system("pause"); 46 return 0; 47 }
4.不重复的随机数
思路:
1: 把你最终需要的结果(不重复的数)预先放在一个数组中, 因为rand函数产生的随机数会重复,我们不直接用,而是用来对应数组的下标
2: rand产生一个随机下标,我们就取出对应数组中的值(我们真正需要的随机数)
3: 然后用数组最后一个值来替换该下标数组中的值
4: 将产生随机下标的范围减少一
5: goto 2
注: 3中所谓数组最后一个值是指产生随机下标范围内的最后一个.
如产生随机下标0-9, 第一次就用array[9]替换,第二次就用array[8]替换.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
五、构造货币
题目描述:
我们都知道人民币的面值是1、2、5、10,为什么是这个数值呢,我们分析了下发现,从1−10的每个数字都可以由每种面值选出至多一张通过加法
和减法(找钱)来构成,(比如:1+2=3,5−1=4,5+1=6,5+2=7,1+2+5=8,10−1=9)
但是实际上,我们只需要1、2、7三种面值就可以组成1−10的每一个数字了
(1+2=3,7−1−2=4,7−2=5,7−1=6,7+1=8,7+2=9,7+1+2=10)
那么现在问题来了,给一个数n,请问最少需要多少种不同的面值就可以构成从1−n的所有数字,注意在构成每一个数字时同种面值不能超过1张。
输入:
一个数字n(1<=n<=100000)
输出:
一个数字,代表最少需要多少种不同的面值可以构成从1−n的所有数字。
样例输入:
10
样例输出:
3
思路:
1 #include <iostream> 2 #include <cstdio> 3 using namespace std; 4 5 int main() 6 { 7 int n,sum=13,cot=3,tem=0; 8 scanf("%d",&n); 9 while(tem+sum<n){ 10 sum+=tem; 11 tem=sum;//cout<<tem<<endl; 12 sum=2*tem+1;//cout<<sum<<endl; 13 cot++; 14 } 15 if(n==1) printf("1"); 16 else if(n<=4) printf("2"); 17 else if(n<=13) printf("3"); 18 else printf("%d",cot); 19 return 0; 20 }
代码2:
1 #include<stdio.h> 2 #include<string.h> 3 #include<algorithm> 4 using namespace std; 5 int main() 6 { 7 int n; 8 while (scanf("%d", &n) != EOF) 9 { 10 int sum = 13, t = 0; 11 int cnt = 3; 12 while (sum + t<n) 13 { 14 sum += t; 15 t = sum; 16 sum = 2 * t + 1; 17 cnt++; 18 } 19 if (n == 1) 20 printf("1\n"); 21 else if (n <= 4) 22 printf("2\n"); 23 else if (n <= 13) 24 printf("3\n"); 25 else printf("%d\n", cnt); 26 } 27 return 0; 28 }
六、序列合并
问题描述:
有两个长度都是N的序列A和B,在A和B中各取一个数相加可以得到N^2个和,求这N^2个和中最小的N个。
输入格式:
第一行一个正整数N;
第二行N个整数Ai,满足Ai<=Ai+1且Ai<=10^9;
第三行N个整数Bi, 满足Bi<=Bi+1且Bi<=10^9.
输出格式:
输出仅一行,包含N个整数,从小到大输出这N个最小的和,相邻数字之间用空格隔开。
输入样例#1:
3
2 6 6
1 4 8
输出样例#1:
3 6 7
分析:这道题和果子合并非常像,每次都是取2个最小的数,但是本题中每个数可以取多次,但是数对不能重复,但是算法还是一样的,利用优先队列.
由于本题中的数据已经排好序了,所以如果选取了坐标为i,j的两个数,那么下一次可能选i+1,j或i,j+1,这样的话由于每个数可以取多次容易重复,所以使用SET判重,这样的话由于要使用两个结构体,比较容易写错.
还有一种比较简单的方式,首先不管怎么样,A序列中的第一个数绝对要选,那么这个数可能和B序列中的任何一个数组成的数对被选,全部加入优先队列中,这样处理了i,j+1的情况,但是还有i+1,j的情况,每次输出一个和之后,将B序列中的第i个数对应的A序列中的第j个数的j++.
怎么解决数对的重复呢?因为输出的是单调的,我们只需要把上次输出的结果和这次的结果比较看是不是相同就好了.
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 #include <cmath> 6 #include <queue> 7 #include <set> 8 9 using namespace std; 10 11 int n, cnt; 12 long long a[100010], b[100010]; 13 struct node //定义一个结构体node(节点) 14 { 15 int x, y; 16 bool operator < (const node &aa) const { //重载<操作符。可以对两个node使用<操作符进行比较 17 return a[x] + b[y] > a[aa.x] + b[aa.y]; 18 } 19 }; 20 21 priority_queue <node> q; 22 23 int main() 24 { 25 scanf("%d", &n); 26 for (int i = 1; i <= n; i++) 27 scanf("%lld", &a[i]); 28 for (int i = 1; i <= n; i++) 29 scanf("%lld", &b[i]); 30 sort(a + 1, a + n + 1); 31 sort(b + 1, b + n + 1); 32 for (int i = 1; i <= n; i++) 33 { 34 node temp; 35 temp.x = 1; 36 temp.y = i; 37 q.push(temp); 38 } 39 long long t = 0; 40 while (cnt != n) 41 { 42 node u = q.top(); 43 q.pop(); 44 if (a[u.x] + b[u.y] != t) 45 { 46 cnt++; 47 printf("%lld ", a[u.x] + b[u.y]); 48 } 49 u.x++; 50 q.push(u); 51 } 52 53 system("pause"); 54 return 0; 55 }
这道题目其实是一道裸的最小堆的题目,而且它给出了Ai<=A(i+1),Bi<=B(i+1),我们连排序都可以不用排,我们建一个堆为C,首先C中存放N个值,分别为A1+B1,A2+B1,...An+B1,然后我们将其建成一个小根堆,每一次取堆顶元素并维护,然后插入Ai+B(j+1)到堆中并维护,因此,我们需要一个记录类型来作为堆,该记录类型有着三个值,分别存储i,j和a[i]+b[j]的值,然后我们以a[i]+b[j]为关键字维护堆即可。