牛客网2017年校招全国统一模拟笔试(第三场)编程题集合


layout: post
title: 牛客网2017年校招全国统一模拟笔试(第三场)编程题集合
date: 2017-06-10
tag: oj

  • 上次做的题只叫一个字惨!刚好抽到了做最后三题,没有一道全部AC掉!
  • 这种题主要是读懂题意,然后抽象成简单的,遇到过得问题。
  • 需要多总结,不然下次还这样!

变换次数

牛牛想对一个数做若干次变换,直到这个数只剩下一位数字。
变换的规则是:将这个数变成 所有位数上的数字的乘积。比如285经过一次变换后转化成2*8*5=80.
问题是,要做多少次变换,使得这个数变成个位数。 
输入描述:

输入一个整数。小于等于2,000,000,000。


输出描述:

输出一个整数,表示变换次数。

输入例子:

285

输出例子:

2
  • 自己写的代码,找了很多细节错误,才全部AC掉,对特殊情况的处理不好!
#include<iostream>
 
using namespace std;
 
int getbitmul(int N)
    {
    int retmul=1;
    if(N>-10&&N<10)
        return N;
    while(N)
        {
        int temp=N%10;
        N/=10;
        retmul*=temp;
    }
    return retmul;
}
 
int main()
    {
    int N;
    int cnt=0;
     
    cin>>N;
    if(N>-10&&N<10)
    {
        cout<<cnt<<endl;
        return 0;
    }else
        {
    int ret = getbitmul(N);
    cnt=1;  
    while(ret/10!=0)
        {
        ret=getbitmul(ret);
        cnt++;
    }  
    }
 
    cout<<cnt<<endl;
  
    return 0;
}
  • 下面参考的就清楚多了
#include<iostream>
using namespace std;
int main()
{
    int num, count, tmp;
    count = 0;
    cin >> num;
    while (num > 9)
    {
        tmp = 1;
        for (; num; num = num / 10)
        {
            tmp = tmp*(num % 10);
        }
        num = tmp;
        count++;
    }
    cout << count << endl;
    return 0;
}

神奇数

  • 题目
给出一个区间[a, b],计算区间内“神奇数”的个数。
神奇数的定义:存在不同位置的两个数位,组成一个两位数(且不含前导0),且这个两位数为质数。
比如:153,可以使用数字3和数字1组成13,13是质数,满足神奇数。同样153可以找到31和53也为质数,只要找到一个质数即满足神奇数。 
输入描述:

输入为两个整数a和b,代表[a, b]区间 (1 ≤ a ≤ b ≤ 10000)。


输出描述:

输出为一个整数,表示区间内满足条件的整数个数

输入例子:

11 20

输出例子:

6
  • AC代码
  • 枚举区间的数字,得到每一位,然后判断组合的数字是否为质数。
#include<iostream>
#include<math.h>
using namespace std;

bool IsPrime(int data)
{
	int i = 0;
	if (data==2)
	{
		return true;
	}
	for (int k = 2;k<=sqrt(data);k++)
	{
		if (data%k==0)
		{
			return false;
		}
	}
	return true;
}

bool IsMagicNum(int num)
{
	int a[5] = { 0 };
	int i = 0; //i为位数
	while (num)
	{
		a[i++] = num % 10;
		num /= 10;  
	}
	int data = 0;
	for (int k = 0; k < i;k++) //用i来限定位数,减少搜索
	{
		for (int j = 0; j < i;j++)
		{
			if (k!=j &&a[k]!=0 && a[j]!=0) //排除0的位数
			{
				data = a[k] * 10 + a[j]; 
				if (IsPrime(data))
				{
					return true;
				}
			}
		}
	}
	return false;
}


int main()
{
	int a, b;
	cin >> a >> b;
	int ret = 0;

	for (int i = a; i <= b;i++)
	{
		if (IsMagicNum(i))
		{
			ret++;
		}
	}
	cout << ret << endl;
	return 0;
}

}

添加字符

  • 题目
牛牛手里有一个字符串A,羊羊的手里有一个字符串B,B的长度大于等于A,所以牛牛想把A串变得和B串一样长,这样羊羊就愿意和牛牛一起玩了。
而且A的长度增加到和B串一样长的时候,对应的每一位相等的越多,羊羊就越喜欢。比如"abc"和"abd"对应相等的位数为2,为前两位。
牛牛可以在A的开头或者结尾添加任意字符,使得长度和B一样。现在问牛牛对A串添加完字符之后,不相等的位数最少有多少位? 
输入描述:

第一行为字符串A,第二行为字符串B,A的场地小于等于B的长度,B的长度小于等于50.字符均为小写字母。


输出描述:

输出一个整数表示A串添加完字符之后,不相等的位数最少有多少位?

输入例子:

abe
cabc

输出例子:

1
  • 分析:情况分为两种:1.A的长度小于B的长度;2.A的长度等于B的长度当小于时,就枚举B串的一个偏移量,然后枚举维护最小的不相等的位数。当等于时,就直接对比就好了。

  • AC代码

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

//想本来想是两个字符串的最小公共子串问题,但是感觉不适用,eg:abc-aec
int main()
{
	string a, b;
	cin >> a >> b; 
	int cnt = 0;
	int ret = 1e9;
	if (a.length()==b.length()) //b>a
	{
		for (int i = 0; i < a.length();i++)
		{
			if (a[i]!=b[i])
			{
				cnt++;
			}
		}
		if (cnt<ret)
		{
			ret = cnt;
		}
	}
	else if (a.length()<b.length())
	{
		for (int i = 0; i < b.length() - a.length()+1;i++)
		{
			for (int j = 0; j < a.length();j++)
			{
				if (a[j]!=b[i+j])
				{
					cnt++;
				}
			}
			if (cnt<ret)
			{
				ret = cnt;
			}
			cnt = 0;
		}
	}
	else
	{
		cout << "a字符串长度大于b的长度!";
	}
	cout << ret << endl;

	return 0;
}

数组变换

  • 题目
牛牛有一个数组,里面的数可能不相等,现在他想把数组变为:所有的数都相等。问是否可行。
牛牛可以进行的操作是:将数组中的任意一个数改为这个数的两倍。
这个操作的使用次数不限,也可以不使用,并且可以对同一个位置使用多次。

输入描述:

输入一个正整数N (N <= 50)
接下来一行输入N个正整数,每个数均小于等于1e9.


输出描述:

假如经过若干次操作可以使得N个数都相等,那么输出"YES", 否则输出"NO"

输入例子:

2
1 2

输出例子:

YES
  • 我是直接排序算的
  • 参考分析:把数组每一个元素都除以2,直到它为奇数。如果此时数组每个元素都一样,满足条件,输出YES
  • AC代码
#include <iostream>
#include<vector>
#include <algorithm>
using namespace std;

int main()
{
	int N;
	cin >> N;

	vector<int> vec;
	int num;
	for (int i = 0; i < N;i++)
	{
		cin >> num;  //正整数没有0
		vec.push_back(num); //也可以在push_back标记最大值,就不需要排序了
	}

	sort(vec.begin(),vec.end());

	bool flag = true;
	for (int i = 0; i < vec.size()-1;i++)
	{
		if (vec[vec.size()-1]%vec[i]!=0 /*&& vec[1]!=0*/)
		{
			flag = false;
			break;
		}
	}
	if (flag)
	{
		cout << "YES" << endl;
	}
	else
	{
		cout << "NO" << endl;
	}
	return 0;
}

排序子序列

  • 题目
牛牛定义排序子序列为一个数组中一段连续的子序列,并且这段子序列是非递增或者非递减排序的。牛牛有一个长度为n的整数数组A,他现在有一个任务是把数组A分为若干段排序子序列,牛牛想知道他最少可以把这个数组分为几段排序子序列.
如样例所示,牛牛可以把数组A划分为[1,2,3]和[2,2,1]两个排序子序列,至少需要划分为2个排序子序列,所以输出2 
输入描述:

输入的第一行为一个正整数n(1 ≤ n ≤ 10^5)

第二行包括n个整数A_i(1 ≤ A_i ≤ 10^9),表示数组A的每个数字。


输出描述:

输出一个整数表示牛牛可以将A最少划分为多少段排序子序列

输入例子:

6
1 2 3 2 2 1

输出例子:

2
  • 参考考虑序列A_1, A_2, . . . , A_i是一个单调的序列.显然如果对于j < i把序列分为A_1, A_2. . . A_j A_j+1, A_j+2, . . . , A_i 两个部分不会使问题变得更优.于是我们可以贪心的去重复下面过程: 1、 从序列中找出最长的单调连续子序列 2、 删除找出的最长的单调连续子序列这里的单调序列包括非递增和非递减,而判断子序列是否单调的时候,注意处理等于的情况。
  • 贪心达到最优
  • AC代码
#include<iostream>
#include <vector>
using namespace std;

int main()
{
	int N;
	cin >> N;
	vector<int> vec;
	int num;
	int ret = 0;
	for (int i = 0; i < N;i++)
	{
		cin >> num;
		if (vec.size()<=1) 
		{
			vec.push_back(num);
		}
		else  //进行在线更新判断
		{
			if (vec[0]<=vec.back()&&vec.back()<=num ) //单调增 
			{
				vec.push_back(num);
			}else if (vec[0]>=vec.back() && vec.back()>=num) //单调减
			{
				vec.push_back(num);
			}
			else  //出现非单调时候,删除已经存在的单调序列
			{
				ret++;
				vec.clear();
				vec.push_back(num);
			}
		}
	}
	cout << ret+1 << endl; //最后一个序列

	return 0;
}

组队竞赛

  • 题目
牛牛举办了一次编程比赛,参加比赛的有3*n个选手,每个选手都有一个水平值a_i.现在要将这些选手进行组队,一共组成n个队伍,即每个队伍3人.牛牛发现队伍的水平值等于该队伍队员中第二高水平值。
例如:
一个队伍三个队员的水平值分别是3,3,3.那么队伍的水平值是3
一个队伍三个队员的水平值分别是3,2,3.那么队伍的水平值是3
一个队伍三个队员的水平值分别是1,5,2.那么队伍的水平值是2
为了让比赛更有看点,牛牛想安排队伍使所有队伍的水平值总和最大。
如样例所示:
如果牛牛把6个队员划分到两个队伍
如果方案为:
team1:{1,2,5}, team2:{5,5,8}, 这时候水平值总和为7.
而如果方案为:
team1:{2,5,8}, team2:{1,5,5}, 这时候水平值总和为10.
没有比总和为10更大的方案,所以输出10.

输入描述:

输入的第一行为一个正整数n(1 ≤ n ≤ 10^5)

第二行包括3*n个整数a_i(1 ≤ a_i ≤ 10^9),表示每个参赛选手的水平值.


输出描述:

输出一个整数表示所有队伍的水平值总和最大值.

输入例子:

2
5 2 8 5 1 5

输出例子:

10
  • 分析:对于所有参赛队员,我们把他们的水平值进行逆序排序,然后逐一挨着安排每个队伍,然后计算出队伍水平值总和,即为所求。
  • AC代码
#include<iostream>
#include <vector>
#include<algorithm>
using namespace std;

int main()
{
	int N;
	cin >> N;

	int ai ;
	vector<int> vec;
	for (int i = 0; i < 3 * N;i++)
	{
		cin >> ai;
		vec.push_back(ai);
	}

	sort(vec.begin(), vec.end());

	long long ans = 0;  //注意输出溢出

	//自己思路:排序后安排中间的N个元素就行了,并不是最大的,AC不过
	
	//需要的是:从N个元素开始,相邻两个开始分配
	for (int i = 0; i < N;i++)
	{
		ans += vec[2*i+N];
	}
	cout << ans << endl;

	return 0;
}

牛牛的数列

  • 题目
牛牛现在有一个n个数组成的数列,牛牛现在想取一个连续的子序列,并且这个子序列还必须得满足:最多只改变一个数,就可以使得这个连续的子序列是一个严格上升的子序列,牛牛想知道这个连续子序列最长的长度是多少。 
输入描述:

输入包括两行,第一行包括一个整数n(1 ≤ n ≤ 10^5),即数列的长度;
第二行n个整数a_i, 表示数列中的每个数(1 ≤ a_i ≤ 10^9),以空格分割。


输出描述:

输出一个整数,表示最长的长度。

输入例子:

6 
7 2 3 1 5 6

输出例子:

5
  • 分析:正着枚举记录一下当前位置的连续上升子序列长度,倒着也做一遍。最后枚举一个连接点即可。
  • AC代码
#include <cstdio>
#include <cstdlib>

const int maxn = 100000 + 5;
const int maxdata = 1e9 + 1;
#define max(a,b)    (((a) > (b)) ? (a) : (b))

int  a[maxn], n;
int pre[maxn], post[maxn];

int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n;i++)
	{
		scanf("%d", &a[i]);
	}
	a[0] = maxdata, a[n + 1] = maxdata;

	for (int i = 1; i <= n;i++)
	{
		pre[i] = a[i - 1] < a[i] ? pre[i - 1] + 1 : 1;
	}

	for (int i = n; i>0;i--)
	{
		post[i] = a[i] < a[i + 1] ? post[i+1] + 1 : 1;
	}

	int ans = 1;
	for (int i = 1; i <= n;i++)
    {
		//ans = max(ans, pre[i - 1] + 1);
		//ans = max(ans, post[i + 1] + 1);
		if (a[i + 1] - a[i - 1] >= 2)
			ans = max(ans, pre[i - 1] + post[i + 1] + 1);
    }
	printf("%d
", ans);
	return 0;
}

训练部队

小牛牛是牛牛王国的将军,为了训练出精锐的部队,他会对新兵进行训练。部队进入了n个新兵,每个新兵有一个战斗力值和潜力值,当两个新兵进行决斗时,总是战斗力值高的获胜。获胜的新兵的战斗力值就会变成对手的潜力值 + 自己的战斗力值 - 对手的战斗力值。败者将会被淘汰。若两者战斗力值一样,则会同归于尽,双双被淘汰(除了考察的那个新兵之外,其他新兵之间不会发生战斗) 。小牛牛想知道通过互相决斗之后新兵中战斗力值+潜力值最高的一个可能达到多少,你能帮助小牛牛将军求出来吗? 
输入描述:

输入包括n+1行,第一行包括一个整数n(1 ≤ n ≤ 10^5);
接下来的n行,每行两个整数x和y(1 ≤ x,y ≤ 10^9)


输出描述:

输出一个整数,表示新兵中战斗力值+潜力值最高的一个能达到多少。

输入例子:

2
1 2
2 1

输出例子:

4

Reference

原文地址:https://www.cnblogs.com/ranjiewen/p/6985537.html