一.京东2018
第一题:疯狂的序列
有一个序列1,2,2,3,3,3,4,4,4,4,5,5,5,5,5....,求第n项是多少?
解法一:暴力法,i从1开始穷举,直到满足i(i+1)/2-i<n<=i(i+1)/2,则输出i,代码如下:
#Include <stdio.h>
int main()
{
long long n;
while (scanf("%lld", &n) != EOF) {
long long i = 1;
while (1) {
if (n > (i*(i+1)/2-i) && n <= i*(i+1)/2) {
printf("%lld\n, i);
break;
}
}
}
return 0;
}
这种做法直接超时,通过率90%.
解法二:二分法,代码如下:
#include <iostream>
using namespace std;
long long n;
int main() {
cin >> n;
long long l = 1, r = 2000000000LL, mid;
while(l < r) {
mid = (l + r) / 2;
if(mid * (mid + 1) / 2 >= n)
r = mid;
else
l = mid + 1;
}
cout << l << endl;
return 0;
}
第二题:神奇的数
如果一个数字拆分之后,分成两组,如果两组的和相等,则为申请的数,例如2323是一个神奇的数,可以分为(2,3),(2,3)两组且和都为5.
解法一:暴力法,拆分之后存在一个数组里面,然后全排列,check两组是否相等即可。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
int l, r;
int ans;
int flag;
while (cin >> l >> r) {
ans = 0;
for (int i = l; i <= r; i++) {
int tmp = i;
flag = 0;
vector<int> vec;
while (tmp) {
vec.push_back(tmp%10);
tmp /= 10;
}
if (vec.size() < 2) {
vec.clear();
continue;
}
sort(vec.begin(), vec.end());
do {
for (int j = 0; j < vec.size() - 1; j++) {
long long part1 = 0;
long long part2 = 0;
for (int m = 0; m <= j; m++)
part1 += vec[m];
for (int k = j + 1; k < vec.size(); k++)
part2 += vec[k];
if (part1 == part2) {
ans++;
vec.clear();
flag = 1;
break;
}
}
if (flag)
break;
} while ((next_permutation(vec.begin(), vec.end())));
}
cout << ans << endl;
}
return 0;
}
解法二:直接在数组里面寻找是否有和为所有数字和的一般的数,不需要全排列,时间复杂度降低很多。代码如下:
#include <iostream>
#include <stack>
#include <string>
#include <algorithm>
using namespace std;
// 求出数组里是否存在 n 个数的和等于某个值 SUM
bool Is_Sum_SomeData(vector<int>data, unsigned int SUM)
{
// 异常输入
int length = data.size();
sort(data.begin(), data.end());
// 创造辅助工具栈 来寻找某些值
// 对栈初始化
unsigned int _sum = 0;
stack<int>S;
S.push(0);
_sum = data[0];
unsigned int now = S.top();
// 核算算法
while (true)
{
// 进栈决策
if (_sum < SUM)
{
// 有元素可以进栈
now++;
if (now < length)
{
S.push(now);
_sum += data[now];
}
// 没有元素可以进栈
else{
// 出口:并且栈空了,那就没有答案了
if (S.empty() == true)
{
return false;
}
// 栈不空时,把栈顶后移一位
else{
now = S.top();
S.pop();
_sum -= data[now];
}
}
}
// 出栈决策
else if (_sum > SUM)
{
// 出口:只有一个值,并且还大于要求的和,那就没有答案了
if (S.size() < 2)
return false;
// 否则出栈寻找个数更小,数更大的值去做
// 出栈两个(进栈一个)
else
{
_sum -= data[S.top()];
S.pop();
_sum -= data[S.top()];
now = S.top();
S.pop();
}
}
// 出口:找到答案
else {
return true;
}
}
}
int main()
{
long long l, r, tmp;
int ans;
int res;
vector<int> vec;
while (cin >> l >> r) {
ans = 0;
for (long long i = l; i <= r; i++) {
res = 0;
vec.clear();
tmp = i;
while (tmp) {
res += tmp%10;
vec.push_back(tmp%10);
tmp /= 10;
}
if (vec.size() < 2)
continue;
if (res % 2 == 1)
continue;
if (Is_Sum_SomeData(vec, res/2))
ans++;
}
cout << ans << endl;
}
return 0;
}
二.滴滴2018
第一题:丑数
解法一:暴力法,对于每一个数都要去判断是否是丑数,代码如下:
class Solution {
public:
bool check(int n)
{
while (n % 2 == 0)
n /= 2;
while (n % 3 == 0)
n /= 3;
while (n % 5 == 0)
n /= 5;
return (n == 1) ? true : false;
}
int GetUglyNumber_Solution(int index) {
if (index <= 0)
return 0;
int i = 0;
int number = 0;
while (i < index) {
++number;
if (check(number)) {
i++;
}
}
return number;
}
};
解法二:暴力法对于每一个数都去判断是否是丑数,其实没必要,可以直接求出所有的丑数,并把它们放在一个数组中,代码如下:
class Solution {
public:
int GetUglyNumber_Solution(int index) {
int *uglyNumber = new int[index];
int *u2 = uglyNumber;
int *u3 = uglyNumber;
int *u5 = uglyNumber;
int nextUglyNumber = 1;
uglyNumber[0] = 1;
while (nextUglyNumber < index) {
int minUglyNumber = min(*u2 * 2, min(*u3 * 3, *u5 * 5));
uglyNumber[nextUglyNumber] = minUglyNumber;
while (*u2 * 2 <= uglyNumber[nextUglyNumber])
u2++;
while (*u3 * 3 <= uglyNumber[nextUglyNumber])
u3++;
while (*u5 * 5 <= uglyNumber[nextUglyNumber])
u5++;
nextUglyNumber++;
}
int ans = uglyNumber[index-1];
delete []uglyNumber;
return ans;
}
};
解法三:和解法二一样也是直接求出所有的丑数,但是用队列来存储丑数,代码如下:
#include <iostream>
#include <queue>
#include <algorithm>
using namespace std;
int main()
{
queue<int> q2,q3,q5;
int n;
int ans=1;
int two, three, five;
q2.push(2);
q3.push(3);
q5.push(5);
cin>>n;
n--;
while(n--){
two = q2.front();
three = q3.front();
five = q5.front();
int mtmp= min(two, min(three, five));
if(mtmp==two){
q2.pop();
q2.push(two*2);
q3.push(two*3);
q5.push(two*5);
}else if(mtmp==three){
q3.pop();
q5.push(three*5);
q3.push(three*3);
}else{
q5.pop();
q5.push(five*5);
}
ans=mtmp;
}
cout<<ans<<endl;
return 0;
}
第二题:
题意晦涩难懂,一个AC的代码如下:
#include <iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main()
{
int cnt;
vector<int> vec;
int begin = 0;
int res=0;
int flag = 0;
while (cin >> cnt)
{
for (int i = 0; i < cnt; i++)
{
int tmp;
cin >> tmp;
vec.push_back(tmp);
}
begin = 0;
res=0;
for (int i = 0; i < cnt; i++)
{
if (vec[i] == 0)
{
res++;
begin = i + 1;
continue;
}
for (int j = begin; j < i; j++)
{
flag = 0;
for (int k = j; k <= i; k++)
{
flag ^= vec[k];
}
if (flag == 0)
{
res++;
begin = i + 1;
continue;
}
}
}
cout << res << endl;
}
return 0;
}
三.美团2017
第一题:大富翁游戏
大富翁游戏,玩家根据骰子的点数决定走的步数,即骰子点数为1时可以走一步,点数为2时可以走两步,点数为n时可以走n步。求玩家走到第n步(n<=骰子最大点数且是方法的唯一入参)时,总共有多少种投骰子的方法。
解法一:利用递推公式f(n)=f(n-1)+f(n-2)+...+f(1)+1,构造递归函数。代码如下:
#include <iostream>
using namespace std;
int
fun(
int
n)
{
if
(n ==
1
)
return
1
;
if
(n ==
2
)
return
2
;
int
ans =
0
;
for
(
int
i =
1
; i <= n-
1
; i++)
ans += fun(n-i);
return
ans +
1
;
}
int
main()
{
int
n;
while
(cin >> n) {
cout << fun(n) << endl;
}
return
0
;
}
解法二:根据状态转移方程f(n)=f(n-1)+f(n-2)+...+f(1)+1,形成动态规划解法。代码如下:
#include <iostream>
#include <vector>
using namespace std;
int
main()
{
int
n;
vector<
int
> dp;
while
(cin >> n) {
dp.assign(n,
0
);
dp[
0
] =
1
;
for
(
int
i =
1
; i < n; i++) {
for
(
int
j =
0
; j < i; j++)
dp[i] += dp[j];
dp[i] +=
1
;
}
cout << dp[n-
1
] << endl;
}
return
0
;
}
解法三:利用递推公式f(n)=f(n-1)+f(n-2)+...+f(1)+1,可以得出通项公式f(n)=2^(n-1),从而直接求解,代码如下:
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
int n;
while (cin >> n) {
cout << pow(2, n-1) << endl;
}
return 0;
}
第二题:拼凑钱币
给你六种面额 1、5、10、20、50、100 元的纸币,假设每种币值的数量都足够多,编写程序求组成N元(N为0~10000的非负整数)的不同组合的个数。
解法一:暴力法,枚举所有的情况,代码如下:
#include <iostream>
using namespace std;
int
main()
{
int
n;
long long
cnt;
while
(cin >> n) {
cnt =
0
;
for
(
int
i =
0
; i <= n/
100
; i++)
for
(
int
j =
0
; j <= n/
50
; j++)
for
(
int
k =
0
; k <= n/
20
; k++)
for
(
int
l =
0
; l <= n/
10
; l++)
for
(
int
m =
0
; m <= n/
5
; m++)
for
(
int
o =
0
; o <= n; o++)
if
(i*
100
+j*
50
+k*
20
+l*
10
+m*
5
+o==n) {
cnt++;
break
;
}
cout << cnt << endl;
}
return
0
;
}
解法二:递归法,其实也是暴力求解,但是代码更简洁。
#include <stdio.h>
#include <vector>
#include <iostream>
using namespace std;
long long fun(vector<int> money, int start, int n)
{
long long ans = 0;;
if (start == money.size()) {
return n == 0 ? 1 : 0;
}
else {
for (int i = 0; i * money[start] <= n; i++) {
ans += fun(money, start+1, n-i*money[start]);
}
}
return ans;
}
int main()
{
vector<int> penny={100,50,20,10,5,1};
int aim;
while (cin >> aim) {
if (aim <= 0) {
cout << "0" << endl;
continue;
}
cout << fun(penny, 0, aim) << endl;
}
return 0;
}
解法三:带状态记录的递归方法,用一个二维数据存放计算过的递归,代码如下:
#include <stdio.h>
#include <vector>
#include <iostream>
using namespace std;
long
long
fun(vector<
int
> money,
int
start,
int
n,
long
long
**map)
{
long
long
ans =
0
;;
if
(start == money.size()) {
return
n ==
0
?
1
:
0
;
}
else
{
for
(
int
i =
0
; i * money[start] <= n; i++) {
if
(map[start+
1
][n-i*money[start]] ==
0
) {
ans += fun(money, start+
1
, n-i*money[start], map);
}
else
{
ans += map[start+
1
][n-i*money[start]];
}
}
}
if
(ans !=
0
)
map[start][n] = ans;
return
ans;
}
int
main()
{
vector<
int
> penny={
100
,
50
,
20
,
10
,
5
,
1
};
int
aim;
while
(cin >> aim) {
if
(aim <=
0
) {
cout <<
"0"
<< endl;
continue
;
}
long
long
**map;
map =
new
long
long
*[penny.size() +
1
];
//使用new int分配内存能够加快程序运行速度?
for
(
int
i =
0
; i < penny.size() +
1
; i++)
{
map[i] =
new
long
long
[aim +
1
];
}
//哈希表初始化
for
(
int
i =
0
; i < penny.size() +
1
; i++)
{
for
(
int
j =
0
; j < aim +
1
; j++)
{
map[i][j] =
0
;
}
}
cout << fun(penny,
0
, aim, map) << endl;
}
return
0
;
}
解法四,动态规划解法,代码如下:
#include <vector>
#include <iostream>
using namespace std;
int
main()
{
vector<
int
> penny={
1
,
5
,
10
,
20
,
50
,
100
};
int
aim;
while
(cin >> aim) {
if
(aim <=
0
) {
cout <<
"0"
<< endl;
continue
;
}
vector<vector<long long>> dp(penny.size() +1, vector<long long>(aim+1, 0));
for(int i = 0; i < penny.size() + 1; i++)
{
dp[i][0] = 1;
}
for(int i = 0; i < aim + 1; i++)
{
if (i % penny[0] == 0)
dp[0][i] = 1;
}
for (int i = 1; i < penny.size() + 1; i++) {
for (int j = 1; j < aim + 1; j++) {
for (int k = 0; k*penny[i] <= j; k++) {
dp[i][j] += dp[i-1][j-k*penny[i]];
}
}
}
cout << dp[penny.size()][aim] << endl;
}
return
0
;
}
解法五,简化的动态规划解法,代码如下:
#include <vector>
#include <iostream>
using namespace std;
int main()
{
vector<int> penny={1,5,10,20,50,100};
int aim;
while (cin >> aim) {
if (aim <= 0) {
cout << "0" << endl;
continue;
}
vector<vector<long long>> dp(penny.size(), vector<long long>(aim+1, 0));
for(int i = 0; i < penny.size(); i++)
{
dp[i][0] = 1;
}
for(int i = 0; i < aim + 1; i++)
{
if (i % penny[0] == 0)
dp[0][i] = 1;
}
for (int i = 1; i < penny.size(); i++) {
for (int j = 1; j < aim + 1; j++) {
if (j >= penny[i])
dp[i][j] = dp[i-1][j] + dp[i][j-penny[i]];
else
dp[i][j] = dp[i-1][j];
}
}
cout << dp[penny.size()-1][aim] << endl;
}
return 0;
}
第三题:最大矩形面积
给定一组非负整数组成的数组h,代表一组柱状图的高度,其中每个柱子的宽度都为1。 在这组柱状图中找到能组成的最大矩形的面积(如图所示)。 入参h为一个整型数组,代表每个柱子的高度,返回面积的值。
只要记录下从任意一个点开始的矩形面积并从中选择最大的一个即可,代码如下:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int n;
vector<int > num;
while (cin >> n){
num.clear();
for (int i = 0; i < n; i++) {
int tmp;
cin >> tmp;
num.push_back(tmp);
}
int minNum = 0;
int max = 0;
int area = 0;
for (int i = 0; i < num.size(); i++) {
minNum = num[i];
for (int j = i; j < num.size(); j++) {
if (num[j] < minNum) {
minNum = num[j];
}
area = (j-i+1)*minNum;
if (area > max)
max = area;
}
}
cout << max << endl;
}
return 0;
}
这种o(n^2)的算法思想很常见,第一个循环遍历每一个下标,第二个循环遍历从第一个循环下标开始后面的元素。
第四题:最长公共连续字串
给出两个字符串(可能包含空格),找出其中最长的公共连续子串,输出其长度。
经典动态规划,其中dp[i][j]表示字符串1以str1[i]结尾与字符串2以str2[j]结尾的最长公共连续字串,代码如下:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
string str1, str2;
int len1, len2;
int ans;
while (getline(cin, str1, '\n') ) {
getline(cin, str2, '\n');
len1 = str1.length();
len2 = str2.length();
ans = 0;
vector<vector<int>> dp(len1+1, vector<int>(len2+1, 0));
for (int i = 1; i <= len1; i++){
for (int j = 1; j <= len2; j++) {
if (str1[i-1] == str2[j-1]) {
dp[i][j] = dp[i-1][j-1] + 1;
}
if (dp[i][j] > ans)
ans = dp[i][j];
}
}
cout << ans << endl;
}
return 0;
}
四.美团2018
第一题:
序列中任意个连续的元素组成的子序列称为该序列的子串。
现在给你一个序列P和一个整数K,询问元素和是K的倍数的子串的最大长度。
比如序列【1,2,3,4,5】,给定的整数K为 5,其中满足条件的子串为{5}、{2,3}、{1,2,3,4}、{1,2,3,4,5},
那么答案就为 5,因为最长的子串为{1,2,3,4,5};如果满足条件的子串不存在,就输出 0。
输入:
第一含一个整数N, 1 ≤ N ≤ 105 。
第二行包含 N 个整数pi ,pi表示序列P第i个元素的值。0 ≤ pi ≤ 105 。
第三行包含一个整数 K, 1 ≤ K≤ 105 。
用暴力法,时间复杂度O(n2),通不过。加了一句,剪枝,还没来得及提交,考试结束,不知道能否通过。代码如下:
#include <stdio.h>
int main()
{
int n,k,ans,res,i,j;
int num[100005];
while (scanf("%d", &n) != EOF){
ans = 0;
for (i = 0; i < n; i++)
scanf("%d", &num[i]);
scanf("%d", &k);
for (i = 0; i < n; i++) {
res = 0;
for (j = i; j < n; j++) {
res += num[j];
if (res % k == 0) {
if (j-i+1>ans)
ans = j-i+1;
}
}
if (ans >= n - i)//剪枝
break;
}
printf("%d\n", ans);
}
return 0;
}
另附别人通过的代码如下:
也有人用同余算法通过,时间复杂度更低,参考链接:
http://blog.csdn.net/u010002184/article/details/77771657
第二题:
考试的时候晕了,题目没看懂。后面看懂了,才觉得确实是比较简单的,只需要降序排列,如果后n-1个数大于第一个数,则满足。否则,不满足。排序也可以省掉。别人通过的代码如下:
剪枝算法参考资料:
http://blog.chinaunix.net/uid-25510439-id-3126204.html
五.深信服2018
第一题:
其中,reverse函数的代码实现如下:
1.
unsigned int reverse(unsigned int num)
{
unsigned int ans = 0;
for (int i = 0; i <= 31; i++) {
num = num >> 1;
int tmp = num & mask;
if (tmp == 1)
ans += pow(2,31-i);
}
return ans;
}
2.
unsigned int reverse(unsigned int num)
{
unsigned int ans = 0;
int i = 0;
while (num != 0) {
int tmp = num & 0x1;
if (tmp == 1)
ans += pow(2,31-i);
i++;
num >>= 1;
}
return ans;
}
第二题:编程填空题,考察堆排序。
代码如下:
static void heap_arrange(int arr[], int cur, int cnt) //调整为小顶堆
{
int heaptop_val = arr[cur]; //堆顶的值
while (cur < cnt) {
int left = 2 * cur + 1;
int right = 2 * cur + 2;
int min = -1;
int min_val =heaptop_val;
if (left < cnt && arr[left] < min_val) { //检查是否比左节点大
min = left;
min_val = arr[left];
}
if (right < cnt && arr[right] < min_val) {//检查是否比右节点大
min = right;
}
if (min == -1)
break;
arr[cur] = arr[min];
cur = min;
}
arr[cur] = heaptop_val;
}
六.巨人2018
第一题:智力编程题题目大意如下:
参考资料:
http://m.blog.csdn.net/github_39542113/article/details/78035170
第二题:判断点是否在三角形之内
参考资料:
http://www.cnblogs.com/graphics/archive/2010/08/05/1793393.html
七.网易2018
第一题:重排序列
小易有一个长度为N的正整数数列A = {A[1], A[2], A[3]..., A[N]}。
牛博士给小易出了一个难题:
对数列A进行重新排列,使数列A满足所有的A[i] * A[i + 1](1 ≤ i ≤ N - 1)都是4的倍数。
小易现在需要判断一个数列是否可以重排之后满足牛博士的要求。
输入描述:
输入的第一行为数列的个数t(1 ≤ t ≤ 10), 接下来每两行描述一个数列A,第一行为数列长度n(1 ≤ n ≤ 10^5) 第二行为n个正整数A[i](1 ≤ A[i] ≤ 10^9)
输出描述:
对于每个数列输出一行表示是否可以满足牛博士要求,如果可以输出Yes,否则输出No。
示例1
输入
2 3 1 10 100 4 1 2 3 4
输出
Yes No
解法一:暴力法,通过率0,....
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
int t;
vector<int> vec;
cin >> t;
for (int i = 0; i < t; i++) {
int n;
int flag = 0;
vec.clear();
cin >> n;
for (int j = 0; j < n; j++) {
int tmp;
cin >> tmp;
vec.push_back(tmp);
}
sort(vec.begin(), vec.end());
do {
int k;
for (k = 0; k < vec.size() - 1; k++){
if ((vec[k]*vec[k+1])%4!=0) {
break;
}
}
if (k == vec.size() - 1) {
cout << "Yes" << endl;
flag = 1;
break;
}
} while ((next_permutation(vec.begin(), vec.end())));
if (!flag)
cout << "No" << endl;
}
return 0;
}
解法二:根据奇数的个数,被2整除的个数,被4整除的个数来求解,代码如下:
#include <iostream>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
int n;
int main() {
int t;
scanf("%d", &t);
while(t--) {
scanf("%d", &n);
int cnt4 = 0;
int cnt2 = 0;
int cnt1 = 0;
for(int i = 0; i < n; i++) {
int x;
scanf("%d", &x);
if(x % 4 == 0) cnt4++;
else if(x % 2 == 0) cnt2++;
else cnt1++;
}
if(cnt2 == 0) {
if(cnt4 >= cnt1 - 1)
printf("Yes\n");
else
printf("No\n");
} else {
if(cnt4 >= cnt1)
printf("Yes\n");
else
printf("No\n");
}
}
return 0;
}