错过的网易2021校招笔试-C++开发工程师 Apare_xzc

错过的网易2021校招笔试-C++开发工程师 2020.8.8


因为腾讯的面试是14.00开始的,没想到面试官热情地面了2个小时,然后网易笔试就没做,直接去做的16点的美团笔试…
晚上在牛客上看到一个老兄的帖子,描述了一下4道题的题意,凭着自己的印象,做了一下。不知道正确与否。


1. 字符串追加字符变成最短的回文串

将一个字符串补成一个回文字符串。
字符串长度n<=1000
返回该回文字符串。
比如:输入“noo”返回“noon”;
输入“moom”返回“moom”;
输入“hello”返回“hellolleh”;

思路:

找到以s[n]结尾的回文串的最大长度ans,然后从s[n-ans],s[n-ans-1]…一直补到s[1]即可。
找ans可以用区间dp

#include <bits/stdc++.h>
using namespace std;
//n <= 1000
//给一个长度为n的字符串,可以往尾部追加任意字符多次,求可以得到的最短的回文串
//noon -> noon
//noo -> noon
//helloworld -> helloworlddrowolleh
const int N = 1002;
char str[N];
int dp[N][N]; 
int main(void) {
	scanf("%s",str+1);
	int ans = 1;
	int n = strlen(str+1);
	if(n==1) { //如果长度为1,则本身是回文 
		cout<<str+1<<endl;
		return 0;
	}
	for(int i=1;i<=n;++i) dp[i][i] = 1;
	for(int i=1;i<n;++i) dp[i][i+1] = (str[i]==str[i+1])?2:0;
	if(str[n]==str[n-1]) ans = 2;
	for(int len=3;len<=n;++len) {
		for(int st=1;st+len-1<=n;++st) {
			int ed = st+len-1; 
			if(str[st]==str[ed]&&dp[st+1][ed-1]!=0) {
				dp[st][ed] = len;
				if(ed==n) ans = max(ans,len);
			}
		}
	}
	//ans代表以最后一个字母为结尾的回文串的最大长度 
	string res = str+1;
	for(int i=n-ans;i>=1;--i) res += str[i];
	cout<<res<<endl;
	return 0;
} 

区间dp的时间复杂度和空间复杂度都是O(n^2), leetcode上有一道题是从首部添加,都差不多,我们可以用KMP。把该串作为文本串,把反转后的串作为模式串,这样匹配到文本串的结尾后,我们就找到了最长的回文后缀

#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
char a[N],b[N];
int Next[N];
int KMP(char * a,int lena,char * b,int * Next) {
	int i=0,j=0;
	while(i<lena) {
		if(j==-1||a[i]==b[j]) ++i,++j;
		else j = Next[j];
	} return j;
}
//fffabcba
//   abcbafff
//把原串作为文本串,reverse后的串作为模式串进行匹配 
void getNext(char * a,int len,int * Next) {
	int i=0,j=-1;
	Next[0] = -1;
	while(i<len) {
		if(j==-1||a[i]==a[j]) ++i,++j,Next[i]=j;
		else j = Next[j];
	}
} 
int main(void) {
	cin>>a;
	int len = strlen(a);
	for(int i=0;i<len;++i) b[i] = a[len-1-i];b[len] = 0; 
	getNext(b,len,Next);
	int j = KMP(a,len,b,Next);
	printf("%s",a);
	for(int i=len-j-1;i>=0;--i) putchar(a[i]);puts("");
	return 0;
} 

2. 去掉价值最小的物品集合使得剩下的物品能分给两个人,价值相同

网易0808第2题
有一堆商品,各自有不同的价值
将这些商品分给两个人,要求两者拥有的价值数一样。
问需要舍弃多少价值的商品。
输入为一堆商品的价值序列。
T组输入(T<10), n<=15, a[i]<=100000

分析:

暴力选择去掉的集合(2^n种,dfs/二进制位压缩),然后每个集合判断可能性。判断可能性可以同样2^n的选,判断是否平分,也可以dp[i][j]代表前i个物品是否能恰好放满容量为j的背包。但容量过大,O(n*v) >>O(2^n)
也可以直接dfs三进制串,0表示给A,1表示给B,看是否相等,然后更新3^15 = 1.4E7

#include <bits/stdc++.h>
using namespace std;
//多组输入 T = 10
//每组n个物品,n<=15,每个物品价值为a[i], a[i]<=100'000
//问去掉价值最少的物品,使得剩下的物品可以平分给两个人价值相同
//先dfs/二进制状压得到所有剩下物品的组合,然后每个组合暴力判断是否能平分。这里暴力比dp快,因为dp范围大 
const int N = 20;
int a[N],n;
//2^15 = 30000
//T = 10, 每组数据,
int r[N];
/*
解决一个问题,已经选出了m个物品,如何确定是否能凑出 sum/2?
法一:dfs爆搜,2^n 每种选择都看是否等于sum/2 (30000)
法二:dp,dp[i][j]表示前i个物品能否放满容量为j的背包,时间复杂度:n*sum(100000)
dp[1000000] = {1,0,0,0,0...0};
for(int i=1;i<=n;++i)
	for(int j=sum;j>=a[i];--j)
		dp[j] = dp[j-a[i]]; 
*/ 
int solve() {
	vector<int> v;
	int sum = 0;
	for(int i=0;i<n;++i) 
		if(r[i]) v.push_back(a[i]),sum+=a[i];
	if(sum&1) return -1;
	sum >>= 1;
	int cnt = v.size(); //cnt个数 
	int tot = 1<<cnt;
	for(int st=0;st<tot;++st) {
		int select = 0;
		for(int i=0;i<cnt;++i) {
			if((st>>i)&1) select+=v[i];
		} 
		if(select==sum) return sum*2;
	}
	return -1;
	
}
int main(void) {
	int T;
	scanf("%d",&T);
	while(T--) {
		int sum = 0;
		scanf("%d",&n);
		for(int i=0;i<n;++i) 
			scanf("%d",a+i),sum+=a[i];
		int ed = 1<<n;
		int res = 0;
		for(int state=0;state<ed;++state) {
			for(int i=0;i<n;++i) 
				r[i] = ((state>>i)&1)?1:0;
			int ans = solve();
			if(ans!=-1) res = max(res,ans);
		}
		cout<<sum-res<<endl;		 
	} 
	
	return 0;
} 

3. 买票问题

网易0808第3题
n人排队购票,
给出:
单人购票用时,
一个人和其后的一个人一起购票用时;
问n个人购票,最少需要用时多少?
售票处开门时间是08:00:00 am,要求输出关门时间(12小时制,下午输出pm,显示格式两个数字)。

分析:每记错的话应该是某oj的原题,貌似之前在vj上做过,dp?

#include <bits/stdc++.h>
using namespace std;
const int N = 100000+100;
int a[N],b[N],dp[N];
//a[i]代表第i个人自己买票的时间
//b[i]代表第i个人和第i+1个人买票的时间 
int main(void) {
	int n;
	cin>>n;
	for(int i=1;i<=n;++i)
		scanf("%d%d",a+i,b+i);
	dp[1] = a[1];
	dp[0] = 0;
	for(int i=2;i<=n;++i) {
		dp[i] = min(dp[i-1]+a[i],dp[i-2]+b[i-1]);
	} 
	//cout<<dp[n]<<endl;
	int h = dp[n]/60;
	int m = dp[n]%60;
	h+=8;
	bool isPm = false;
	if(h>12) h-=12,isPm = true;
	printf("%02d:%02d:00 %s\n",h,m,isPm?"pm":"am"); 
	return 0;
} 

4. 相互认可的对数

网易0808笔试第4题
给出的是教授们认可信息,一组[1,2]表示1认可2,
如果有[1,2],[2,3],则可以认为1认可3.
问在给出信息中,互相认可的对数有多少?
n<=100000

分析:求有向图的强联通分量。每个强连通分量里的num个人相互认可。num*(num-1)/2 求和即可。

用tarjan求强连通分量。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+100;
struct Node{
	int to,Next;
}node[maxn];
int head[maxn],tot,cnt;
void init() { //初始化链式前向星 
	tot = 0;
	memset(head,-1,sizeof(head));
}
void addedge(int u,int v) {
	node[tot].to = v;
	node[tot].Next = head[u];
	head[u] = tot++;
} 
stack<int> st;
bool instack[maxn];
int dfn[maxn];
int low[maxn];
long long res;
void tarjan(int u) {
	st.push(u);
	instack[u] = true;
	dfn[u] = low[u] = ++cnt;
	for(int i=head[u];i!=-1;i=node[i].Next) {
		int v = node[i].to;
		if(!dfn[v]) {
			tarjan(v);
			low[u] = min(low[u],low[v]);
		} 
		else if(instack[v])
			low[u] = min(low[u],dfn[v]); 
	}
	if(dfn[u]==low[u]) {
		int num = 0;
		while(1) {
			++num;
			int x = st.top();st.pop();
			instack[x] = false;
			if(x==u) break;
		}
		//num为该强连通分量中结点的个数,他们都彼此认同,因此两两一对 
		res += 1ll*num*(num-1)/2; 
	}
}
int main(void) {
	int n,m,u,v;
	cin>>n>>m;
	init();
	for(int i=1;i<=m;++i) {
		scanf("%d%d",&u,&v);
		addedge(u,v); 
	}
	res = 0;
	for(int i=1;i<=n;++i)
		if(!dfn[i]) tarjan(i);
	printf("%lld\n",res); 	
	return 0;
} 

错过了网易笔试很可惜。题目质量挺高的。不知道这些代码能拿多少分, 希望深夜补做可以有所收获。
2020.8.9 1:44
xzc


猜你喜欢

转载自blog.csdn.net/qq_40531479/article/details/107888952