您的位置:首页 > 编程语言

2017微软秋季校园招聘在线编程笔试

2016-10-11 10:41 465 查看
别的不多说了,记录一下题目吧。找工作有很大一部分因素是缘分,莫强求,保持好心态~

只A了前三道:

第一题:

题意:给出n个数(n≤1000000),可以任意选择两个相邻的数字,如果它们相加的和是奇数,那么可以将这两个数删除。问最后可能剩下的最少的数的数量。

思路:细想可知最后剩下的数的数量是确定的。比如考虑有x个奇数,y个偶数(x>y),那么必然存在某个偶数和奇数相邻可删除。所以数量少的必定最后会全部被删除。最后的答案也就是奇数的数量和偶数数量之差的绝对值咯。

代码如下:

#include <iostream>
#include <cmath>
using namespace std;
int main(){
int n,x,res = 0;
scanf("%d",&n);
for(int i = 0;i<n;i++){
scanf("%d",&x);
res += (x&1);
}
printf("%d\n",abs((res<<1)-n));
return 0;
}

第二题:

题意:给出一个长度为n(n<100000)的只包含小写字符的字符串s[0...n-1],再给出m(m<=200)个字符对(如:ab),表示ab和ba不能出现在串中(后文称这个不能出现的集合为黑名单。问最少删除几个字符能够使得输入串是合法的?

思路:只需求合法的串的最长可能。明显思路是dp。一个显然的一维dp是设dp[x]表示考虑前s[0...x]中,且最后一个字符必须是s[x]的情况下的最长可能。那么dp[x]=max(dp[j]+1),0<=j<=x-1且(s[j],s[x])不出现在黑名单中。两点说明:第一,dp的值不是递增的,所以不能从大到小遍历j,找到第一个(s[j],s[x])不在黑名单中就退出。这一点可以考虑如下例子:acccbd,黑名单是(c,b)。第二:这个dp 的复杂度显然是O(n^2),会超时。

还是考虑上述dp,有没有考虑重复的情况呢?其实是有的,在遍历j的过程中,s[j]处可能出现多个相同的字符的情况,实际上这只需要考虑最长的那个即可。这给出了一个启示:只需要维护以特定字符结尾的最长的数量即可。所以dp设计为:dp(i,j)表示考虑前i的字符的子串且最后一个字符为j(0<=j<=25)的最长可能。那么实际上每遇到一个新的s[i],只需要更新以s[i]为最后一个字符的情况,其他的情况都未变。状态方程为:如果j表示的是s[i]的字符:那么dp[i][j] = max(dp[i-1][k]+1),0<=k<=25且(('a'+k),
s[i])不出现在黑名单中;否则dp[i][j] = dp[i-1][j]。这个动归的复杂度是O(26n),符合要求。实际上空间上可以只开一个26大小的数组,每次只更新s[i]对应字符位置的dp。

代码如下:

#include <iostream>
#include <cstring>
#include <set>
#define N 100005
#define INF 0x3fffffff
using namespace std;
int n,m;
int dp[27];
bool hh[27][27];
int main(){
cin >> n;
string s;
cin >> s;
scanf("%d",&m);
for(int i = 0;i<m;i++){
getchar();
char c1,c2;
c1 = getchar();
c2 = getchar();
hh[c1-'a'][c2-'a'] = hh[c2-'a'][c1-'a'] = true;
}
int res = 0;
dp[s[0]-'a'] = 1;
for(int i = 1;i<n;i++){
int j = s[i]-'a';
if(!hh[j][j])//先更新dp[j],这样后面再更新就不会影响dp[j]了
dp[j]++;
else
dp[j] = max(dp[j], 1);
for(int k = 0;k<26;k++)
if(k != j && !hh[k][j])
dp[j] = max(dp[j], dp[k]+1);
}
for(int i = 0;i<26;i++)
res = max(res,dp[i]);
cout << n-res << endl;
return 0;
}


第三题:

题意:n个学生(n<=10000)m个(m<=100)地方。每个学生i有一个唯一的id,以及到达校门的时间ti。而且他要按顺序去pi个地方办理手续,这个序列用用(oij,wij)表示,也就是第j个地方要去的是oij这个点,而且要呆wij长时间。已知从校门去到每个地方以及地方和地方之间的转移时间都是k。每个地方只能给一个学生办理手续,办理的次序按照先来先服务,如果同时到达id小的优先。问每个学生结束办理手续的时间分别是多少。

思路:对这个题目非常失望,看描述以为是一道图论题,实际上就是模拟,没什么技术含量。把该表示的表示清楚,用队列按照时间顺序挨个处理就好了。给我的一个教训就是,结构体的构造和析构非常耗时,如果不需要这种构造和析构,那么考虑使用指针。

代码如下:

#include <cstdio>
#include <cstring>
#include <string>
#include <cstdlib>
#include <vector>
#include <queue>
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include <iostream>
#define N 10005
#define INF 0x3fffffff
using namespace std;
int n,m,k;
struct node{
int now,id;
int index;
int len;
int v;
vector<int> s;
vector<int> t;
node(){
now = id = index = len = v = 0;
};
};
node *p
;
int res
,beg[105];
struct cmp{
bool operator()(node *a,node *b){
if(a->now == b->now)
return a->id > b->id;
return a->now>b->now;
}
};
priority_queue<node*, vector<node*>, cmp> q;
int main(){
scanf("%d %d %d",&n,&m,&k);
for(int i = 0;i<n;i++){
int num,a,b;
p[i] = new node();
scanf("%d %d %d",&p[i]->id,&p[i]->now,&num);
p[i]->v = i;
p[i]->len = num;
for(int j = 0;j<num;j++){
scanf("%d %d",&a,&b);
p[i]->s.push_back(a);
p[i]->t.push_back(b);
}
p[i]->index = 0;
p[i]->now += k;
q.push(p[i]);
}
while(!q.empty()){
node *tmp = q.top();
q.pop();
int b = max(tmp->now, beg[tmp->s[tmp->index]]);
tmp->now = beg[tmp->s[tmp->index]] = b+tmp->t[tmp->index];
if(tmp->index == tmp->len-1){
res[tmp->v] = tmp->now;
}else{
tmp->now += k;
tmp->index++;
q.push(tmp);
}
}
for(int i = 0;i<n;i++)
printf("%d\n",res[i]);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息