牛客网-网易2018校园招聘编程题真题集合-解题思路及源码

本文精选了8道算法题目,涵盖动态规划、字符串处理、数学计算等核心算法领域,每题均提供详细的解题思路与代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

因为牛客网上的题目内容有版权声明,暂时就不复制题目了,需要的同学请自行登录牛客网获取。

1、魔法币

思路:分析题目中发现,魔法机器1只能产生奇数的魔法币,而魔法机器2只能产生偶数的魔法币。本题目采用从结果倒推的思路。比如例子中需要10个魔法币,那么10个魔法币肯定是通过魔法机器2产生,需要给魔法机器2投入4个魔法币才能产生10个。要产生4个魔法币还是要通过魔法机器2,那么需要将1个魔法币投入到魔法机器2种,要产生1个魔法币需要投入0个魔法币到魔法机器1.那么通过倒推后,反向输出就可。

源码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=35;
int main()
{
	long long n;
	while(cin>>n&&n)
	{
		int ans[maxn],i=0;
		while(n!=0)
		{
			if(n%2==0)
			{
				n=(n-2)/2;
				ans[i++]=2;
			}
			else
			{
				n=(n-1)/2;
				ans[i++]=1;
			}
		}
		for(int j=i-1;j>=0;j--)
		{
			cout<<ans[j];
		}
		cout<<endl;
	}
	
	return 0;
} 
2、相反数

思路:输入一个整形的数据后,我们通过取余的方式得到输入数据的个位、十位等以及位数,再反向进行进行计算后相加就行,实际上可能采用字符数组输入更快。

源码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=20;
int main()
{
	int n;
	while(cin>>n&&n)
	{
		int i=0,m=n;
		int ans[maxn];
		while(n)
		{
			ans[i]=n%10;
			n=n/10;
			i++;
		}
		int xfs=0;
		for (int j=0;j<i;j++)
		xfs +=ans[j]*pow(10,i-j-1);
		cout<<xfs+m<<endl;
	}
	return 0;
}
3、字符串碎片

思路:实际上不需要统计每个碎片的数量,所有碎片的和就为字符串的长度,只需要知道这个字符串被分成了多少个碎片就行。

源码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
	string s;
	while(getline(cin,s))
	{
		int np=1;
		char p;
		p=s[0];
		for(int i=0;i<s.length();i++)
		{
			if(p==s[i])
			{} 
			else
			{
				np++;
				p=s[i];
			}
		}
		printf("%.2f\n",(double)s.length()/np);
	}
	return 0;
} 
4、游历魔法王国

思路:本题目在构建树的时候,是一种特殊构建方法,保证了树中的每一个元素都能够连通。实际上,在构建的过程中,就能够找到从0点出发到叶子节点的最长路径,那么如果步数小于等于最长路径,那么答案就为最长路径,如果步数大于最长路径,可以进行折返,而折返的到达另外一个叶子节点至少需要两步。本题目的测试数据不是很严谨,直接将剩余的步数除以2加上最长路径的步数就可以得到答案。(本题参考了其余网页)

源代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 50 + 5;
int n, L;
int parent[maxn];
int dp[200];
int main() {
    scanf("%d%d", &n, &L);
    for(int i = 0; i < n - 1; i++) scanf("%d", &parent[i]);
    int mx = 0;
    for(int i = 0; i < n - 1; i++) {
        dp[i + 1] = dp[parent[i]] + 1;
        mx = max(mx, dp[i + 1]);
		//因为题目给出了一种特殊的树,可以进行这种方式获得最长路径 
    }
    int d = min(L, mx);
    cout << min((n), 1 + d + (L - d) / 2) << endl;
    return 0;
}
5、重排数列

思路:本题目实际上可以变为在数组中任选两个数相乘能够被4整除。那么首先能够找到满足

(1)被4整除的数乘以任何数都能被4整除;

(2)两个能够被2整除的数相乘就能被4整除。

那么直接统计数字中符合这两种特征数的数量。进行判断就好。

源码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
	//freopen("datain.txt","r",stdin);
	int t;
	cin>>t;
	while(t--)
	{
		int n,d4=0,d2=0,rest=0;
		cin>>n;
		for(int i=0;i<n;i++)
		{
			int tmp;
			cin>>tmp;
			if(tmp%4==0) d4++;
			else if(tmp%2==0) d2++;
			else rest++;
		}
		if(d2%2==1)
			rest++;
		if(rest-1<=d4)
		cout<<"Yes"<<endl;
		else
		cout<<"No"<<endl;	
	}
	
	return 0;
} 
6、最长公共子括号序列

思路:本题咋一看可以通过直接枚举符合要求的括号序列,然后再计算LCS的进行,但是由于字符串的长度可以达到50。这样只能通过测试样例的50%。LCS采用DP实现。

源码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 50+10;
int d[maxn][maxn],ans=0,maxans=0;
string s;
int n;
int  LCS(char *A)
{
	d[0][0]=0;
	for(int i=0;i<n;i++)
		for(int j=0;j<n;j++)
		{
			if(A[i]==s[j])
			d[i][j]=d[i-1][j-1]+1;
			else 
			d[i][j]=max(d[i-1][j],d[i][j-1]);
		}
	return d[n-1][n-1];
}
void print_permutation(char * A,int cur)  
//n为数组A的长度,A为待排列的数组,cur为A中欲确定元素的位置   
{  
    if(cur==n)//所有元素已经确定完成,输出。   
    {  
    	int tmp=LCS(A);
    	if(tmp==maxans)
		{
			ans++;
		}
		if(tmp>maxans&&tmp!=n)
		{
			maxans=tmp;ans=1;
		} 
    }  
    else 
	{
    //A中的元素没有确定完,尝试在A[cur]填元素   
        int cntq=0,cnth=0;  
        for(int j=0;j<cur;j++)  
        {  
            if(A[j]=='(') //-1为前括号,如果大于0,。 
            	cntq++;
            else
            	cnth++;
        }  
        if(cntq>cnth&&cntq<(n/2))  
        {  
            A[cur]='('; 
            print_permutation(A,cur+1);
			A[cur]=')';
			print_permutation(A,cur+1);
        }
		else if(cntq>cnth&&cntq==(n/2))
		{
			A[cur]=')';
			print_permutation(A,cur+1);	
		}
		else
		{
			A[cur]='(';
			print_permutation(A,cur+1);
		} 
	}
        
}  


int main()
{

	char  A[maxn];
	cin>>s;
	n=s.length();
	print_permutation(A,0); 
	cout<<ans;
	return 0;
} 
本题目看似很麻烦, 实际上,我们可以得出最长的公共子串的长度为n-1(n输入字符串的长度),那么我们可以在输入的字符串s上,选取s的字符,插入到不同的位置来判断是否满足括号匹配。如果匹配就将它存起来。最后数没有重复的数量就行。举个例子如果输入是s[]="(())()";那么首先将s[1]取出来,将s[1]放在2的位置上,这样就产生一个新的字符串s1。这样s1[0]==s[0],s1[1]==s[2],s1[2]==s1[1]....后面的相等。然后判断s1是否符合括号匹配,如果符合存入map中,这样可以枚举的字符从s[1]到s[n-1],枚举的位置从1到n-1.最后统计map中元素的数量就可以得到答案。

源码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 50+10;
bool judge(char s1[],int n)
{
	stack<char> stk1;
	for(int i=0;i<n;i++)
	{
		if(s1[i]=='(')
			stk1.push(s1[i]);
		else if(!stk1.empty())
			stk1.pop();
		else
			return false;
	}
	return true;	
}
int main()
{
	//freopen("datain.txt","r",stdin);
	char s[maxn],s1[maxn];
	cin>>s;
	int ans=-1,n;
	n=strlen(s);
	s[n]='\0';
	map<string,int> mp1;
	mp1[s]=1;
	for(int i=1;i<n;i++)
	{
		for(int j=1;j<n;j++)
		{		
			int nl=0;
			s1[nl++]=s[0];		
			for(int k=1;k<j;k++)
			{
				if(k!=i)
				{
					s1[nl++]=s[k];
				}
				
			}
			s1[nl++]=s[i];
			for(int k=j;k<n;k++)
			{
				if(k!=i)
				{
					s1[nl++]=s[k];
				}
				
			}
			s1[n]='\0';
			//cout<<s1<<endl;
			if(judge(s1,n))
			{
				if(!mp1.count(s1))
					mp1[s1]=1;
			} 
			
		}
		
	}
	for(map<string,int>::iterator it = mp1.begin(); it!=mp1.end();it++) ans++;
	cout<<ans<<endl;	
	return 0;
} 
7、合唱

思路:典型动态规划题,设置状态为d[i][j]为小Q最后一个唱第i个音,牛博士唱第j个音的最小难度。首先设定m=max(i,j)+1;状态转换为:d[i][j]=min(d[m][j]+abs(v[m]-v[i],d[i][m]+abs(v[m]-v[j]));d[i][j]的值依赖后面的值。

源码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=2000+10;
int d[maxn][maxn],v[maxn];
int n;
int dp(int i,int j)
{
	int m = max(i,j)+1;
	if(m>n) return 0;
	if(d[i][j]!=-1) return d[i][j];
	return d[i][j]=min(dp(m,j)+(i>0?abs(v[m]-v[i]):0),dp(i,m)+(j>0?abs(v[m]-v[j]):0));
	
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
		cin>>v[i];
	for(int i=0;i<maxn;i++)
		for(int j=0;j<maxn;j++)
			d[i][j]=-1;
	cout<<dp(0,0)<<endl;
	
	return 0;
} 
8、射击游戏

思路:本题目将怪物的旋转转化为旋转坐标轴,枚举角度的方法,可以证明,想要消灭的怪物最多,那么旋转坐标轴后,至少有两点与坐标轴(X轴或Y轴)平行。那么可以通过计算输入坐标,两点连线与x轴的夹角,来获得需要枚举的角度。旋转后,通过平行公式判断与X轴和Y轴平行的怪物的数量,加起来,就为消灭最多怪物的数量。

源码:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 50 + 5;
int x[maxn], y[maxn];
int n;
int solve() {
    if(n <= 2) return n;
    int ans=2;
    double x1[maxn],y1[maxn];
    //确定枚举的角度 
	double angles;
	for(int i=0;i<n;i++)
		for(int j=0;j<i;j++)
		{
			int cnt=0,vis[maxn];
			for(int q=0;q<maxn;q++) vis[q]=1;
				angles=atan((double)(y[i]-y[j])/(x[i]-x[j]));
				for(int k=0;k<n;k++)
				{
					x1[k]=x[k]*cos(angles)+y[k]*sin(angles);
					y1[k]=y[k]*cos(angles)-x[k]*sin(angles);
				}
				for(int z=0;z<n;z++)
				{
					if(fabs(y1[z]-y1[i])<0.001&&vis[z])
					{
						cnt++;
						vis[z]=0;	
					}
					 	
				}
				int sumpy=0;
				for(int z=0;z<n;z++)
				{
					if(vis[z])
					{
						vis[z]=0;	
						int py=1;
					for(int p=0;p<n;p++)
					if(fabs(x1[z]-x1[p])<0.001&&vis[p])
					{
						py++;
						vis[p]=0;	
					}
					sumpy=max(sumpy,py);
					}
				}
				cnt+=sumpy;
				ans=max(ans,cnt);				
		}
	//根据枚举的角度进行旋转
    return ans;
}
int main() {
	//freopen("datain.txt","r",stdin);
    cin >> n; 
    for(int i = 0; i < n; i++) cin >> x[i];
    for(int i = 0; i < n; i++) cin >> y[i];
    cout << solve() << endl;
}








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值