9.27网易互娱游研笔试4道编程题满分题解
前三题对我来说比较容易把,可能对有些同学有点难度,一直顺风顺水,大概前两题做完还剩2小时,然后03花了50分钟, 最后一题因为细节,卡了最后剩10分钟才满分,这里分享下做法。
01题
让你列A * B的竖式,然后算出现的0-9每个数字的频率,最后还要求出现最多的那个。
比如11 * 12
11
12
—
22
11
132
2出现了4次。
老实说,这题我一开始扫了一眼,大概就是个暴力列竖式就好了,没看到0是不算的,因此我是按算0的来求的(代码会复杂)
最后跑样例发现是不用的,因为如果有0的话,这题难度会高一点,那我就按有0的情况说了。
首先可以发现,由于他要求A * B的竖式,必须是A在上面,B在下面,找规律容易发现,其实就是取B的每一位跟A去乘,得一个C,然后算一下C的每一位数字是多少,cnt[i]++即可
最后要注意,A和B本身要被记录一次,A*B也要被记录一次。然后输出cnt数组就好。
由于要算全部n个竖式的频率,那就再开个sum数组记一下(cnt每次一个竖式情况,sum永远不清空),输出即可,代码如下:
难度提升:假如题目要求考虑0,你必须注意,while会碰到0直接不进入,因为会发现无法把0计数进来,解决的办法很容易,加个bool变量,强制进一次,或者do while循环即可。
#include <stdio.h> #include <vector> #include <algorithm> #include <string.h> #include <limits.h> #include <string> #include <iostream> #include <queue> #include <math.h> #include <map> #include <stack> #include <set> #include <list> #include <forward_list> #define left (now<<1) #define right ((now<<1)+1) #define mid ((l + r) >> 1) #define midmid ((r + mid) >> 1) #define LONG_LONG_MIN -9223372036854775808ll #define LONG_LONG_MAX 9223372036854775807ll using namespace std; typedef long long int ll; const int MAXN = 1e5 + 310; const int MOD = 1e9 + 7; int n,a,b; int cnt[10],sum[10]; int main(){ scanf("%d",&n); memset(sum,0,sizeof(sum)); for(int i = 1; i <= n; ++i){ memset(cnt,0,sizeof(cnt)); scanf("%d%d",&a,&b); int nb = b; bool flag = true; while(nb != 0 || flag){ flag = false; int now = nb % 10; nb /= 10; bool flag2 = true; int c = a * now; while(c != 0 || flag2){ flag2 = false; cnt[c % 10]++; c /= 10; } } flag = true; int c = a * b; while(c != 0 || flag){ flag = false; cnt[c % 10]++; c /= 10; } flag = true; while(a != 0 || flag){ flag = false; cnt[a % 10]++; a /= 10; } flag = true; while(b != 0 || flag){ flag = false; cnt[b % 10]++; b /= 10; } for(int i = 1; i <= 9; ++i){ sum[i] += cnt[i]; i == 9 ? printf("%d\n",cnt[i]) : printf("%d ",cnt[i]); } } int ans,maxn; maxn = -1; for(int i = 1; i <= 9; ++i){ if(sum[i] > maxn){ maxn = sum[i]; ans = i; } } printf("%d\n",ans); return 0; }02题
一看这才6个键,枚举全排列才6!=720,这还有啥好说的,直播暴力枚举全排列,每种情况答案取min即可。
当时迟疑了一会,虽然这题很容易,但是我觉得应该有动态规划解法,来解决键更多的情况,但是他既然没要求那么难,就暴力好了。
C++有自己可以枚举排列的函数,可以用用减少代码量。
update:02有人回复说看不大懂代码,我这边详细解释下我做法吧。
首先为了处理方便,ASDFGH 6个键,你压根没必要真的就ASDFGH这样存,可以当作是1 2 3 4 5 6,丢进数组a里,对a枚举全排列就好了。
那么对于某个排列,比如2 4 3 1 6 5,我建一个数组bi,表示第i个数在a中的位置(其实有点置换的概念),比如此时b4 = 2,b6 = 5
然后我去枚举这个打字序列s,记录一下上一次手指的位置,比如一开始是1,那么下一个要打的字是6,b6 = 5,那么abs(b6 - 1)就是此次手指移动的代价,last改成b6,因此手指此时放在b6上面了
next_permutation
algorithm库中自带的函数,用于生成全排列,其底层算法我记得没错的话,比你自己手写效率是要高一点的。在《组合数学》这本书中有详细展开全排列生成算法,包括格雷码等等,有兴趣的可以了解一下。
如果不用这个函数,这部分你得写一个dfs(深度优先搜索)去枚举一下。
#include <stdio.h> #include <vector> #include <algorithm> #include <string.h> #include <limits.h> #include <string> #include <iostream> #include <queue> #include <math.h> #include <map> #include <stack> #include <set> #include <list> #include <forward_list> #define left (now<<1) #define right ((now<<1)+1) #define mid ((l + r) >> 1) #define midmid ((r + mid) >> 1) #define LONG_LONG_MIN -9223372036854775808ll #define LONG_LONG_MAX 9223372036854775807ll using namespace std; typedef long long int ll; const int MAXN = 1e3 + 10; const int MOD = 1e9 + 7; int t,a[10],b[10],len; char s[MAXN]; int pd(){ int last = 1; int re = 0; for(int j = 0; j < len; ++j){ re = re + abs(b[s[j] - '0'] - last); last = b[s[j] - '0']; } return re; } int main(){ scanf("%d",&t); while(t--){ scanf("%s",s); len = strlen(s); for(int i = 0; i < len; ++i){ switch(s[i]){ case 'A': s[i] = '1'; break; case 'S': s[i] = '2'; break; case 'D': s[i] = '3'; break; case 'F': s[i] = '4'; break; case 'G': s[i] = '5'; break; case 'H': s[i] = '6'; break; } } for(int i = 1; i <= 6; ++i){ a[i] = i; b[i] = i; } int ans = INT_MAX; while(1){ ans = min(ans,pd()); if(!next_permutation(a + 1,a + 7)){ break; } for(int i = 1; i <= 6; ++i){ b[a[i]] = i; } } printf("%d\n",ans); } return 0; }03
又是一道没营养的没算法题,一眼就看出来是个大模拟题,这种题就不考察你算法,考代码的基本功。
想好流程就好了,先对地图bfs每个连通块,找到那个题目要求的那个,然后暴力再bfs一次,把那块全置-1,然后先掉落,再左移即可。
#include <stdio.h> #include <vector> #include <algorithm> #include <string.h> #include <limits.h> #include <string> #include <iostream> #include <queue> #include <math.h> #include <map> #include <stack> #include <set> #include <list> #include <forward_list> //#define left (now<<1) //#define right ((now<<1)+1) //#define mid ((l + r) >> 1) //#define midmid ((r + mid) >> 1) #define LONG_LONG_MIN -9223372036854775808ll #define LONG_LONG_MAX 9223372036854775807ll using namespace std; typedef long long int ll; const int MAXN = 1e3 + 10; const int MOD = 1e9 + 7; struct s1{ int x,y; int num; int id; }; int a[60][30]; int n,m,t; char s[30]; s1 maxn,dnow; bool vis[60][30]; queue<int> qx,qy; int mv[4][2] = {{0,1},{0,-1},{-1,0},{1,0}}; void bfs(int x,int y){ dnow.num = 1; dnow.x = x; dnow.y = y; dnow.id = a[x][y]; qx.push(x); qy.push(y); vis[x][y] = true; while(!qx.empty()){ int nowx = qx.front(); qx.pop(); int nowy = qy.front(); qy.pop(); if(nowx < dnow.x){ dnow.x = nowx; dnow.y = nowy; }else if(nowx == dnow.x && nowy < dnow.y){ dnow.x = nowx; dnow.y = nowy; } for(int i = 0; i < 4; ++i){ int nextx = nowx + mv[i][0]; int nexty = nowy + mv[i][1]; if(vis[nextx][nexty] == false && a[nextx][nexty] == a[nowx][nowy]){ vis[nextx][nexty] = true; dnow.num++; qx.push(nextx); qy.push(nexty); } } } } bool pd(){ maxn.num = -1; maxn.id = INT_MAX; for(int i = 1; i <= n; ++i){ for(int j = 1; j <= m; ++j){ vis[i][j] = true; if(a[i][j] != -1){ vis[i][j] = false; } } } for(int i = 1; i <= n; ++i){ for(int j = 1; j <= m; ++j){ if(vis[i][j] == false){ bfs(i,j); if(maxn.num < dnow.num){ maxn = dnow; }else if(maxn.num == dnow.num){ if(maxn.id > dnow.id){ maxn = dnow; }else if(maxn.id == dnow.id){ if(maxn.x > dnow.x){ maxn = dnow; }else if(maxn.x == dnow.x && maxn.y > dnow.y){ maxn = dnow; } } } } } } return maxn.num >= 2; } void poke(){ qx.push(maxn.x); qy.push(maxn.y); a[maxn.x][maxn.y] = -1; while(!qx.empty()){ int nowx = qx.front(); qx.pop(); int nowy = qy.front(); qy.pop(); for(int i = 0; i < 4; ++i){ int nextx = nowx + mv[i][0]; int nexty = nowy + mv[i][1]; if(a[nextx][nexty] == maxn.id){ a[nextx][nexty] = -1; qx.push(nextx); qy.push(nexty); } } } } void down(){ for(int i = n - 1; i >= 1; --i){ for(int j = 1; j <= m; ++j){ if(a[i][j] == -1){ continue;} int nowx = i; int nowy = j; while(nowx < n && a[nowx + 1][nowy] == -1){ swap(a[nowx + 1][nowy],a[nowx][nowy]); ++nowx; } } } } void left(){ bool none = true; for(int i = 1; i <= n; ++i){ if(a[i][1] != -1){ none = false; break; } } for(int j = 2; j <= m; ++j){ if(none){ for(int i = 1; i <= n; ++i){ swap(a[i][j],a[i][j - 1]); } }else{ none = true; for(int i = 1; i <= n; ++i){ if(a[i][j] != -1){ none = false; break; } } } } } void ceshi(){ for(int i = 1; i <= n; ++i){ for(int j = 1; j <= m; ++j){ j == m ? printf("%d\n",a[i][j]) : printf("%d ",a[i][j]); } } printf("\n"); } int main(){ scanf("%d",&t); while(t--){ scanf("%d%d",&n,&m); memset(vis,true,sizeof(vis)); memset(a,-1,sizeof(a)); for(int i = 1; i <= n; ++i){ scanf("%s",s); for(int j = 0; j < m; ++j){ a[i][j + 1] = s[j] - '0'; } } while(pd()){ poke(); //printf("after poke:\n"); ceshi(); down(); //printf("after down:\n"); ceshi(); left(); //printf("after left:\n"); ceshi(); } int ans = 0; for(int i = 1; i <= n; ++i){ for(int j = 1; j <= m; ++j){ if(a[i][j] != -1){ ++ans;} //printf("%d ",a[i][j]); } } //ceshi(); printf("%d\n",ans); } return 0; } /** 1 4 4 0322 0310 1210 3220 **/04题
暴力O(n ^ 2),正解是O(nlogn)
先说说暴力,一开始10分钟我看错题目了,我以为是随机给的矩形,根本没想法(30%的当然好做,直接枚举每对矩形其实可以的),后来发现,原来都是有个顶点在原点的。
一看对题目,思路就来了,既然都在原点,我们会发现,假如一个矩形在第一象限,左下角肯定是原点对吧,那么只有另一个矩形也在第一象限,他们才可能重叠,会产生新的矩形。
因此把4个象限的矩形分开放4个数组里,然后取一下绝对值,转到第一象限,那么分开算4次就行了,这样问题就变成了,矩形一定会出现在第一象限,然后balabala算
那么怎么算呢?我当时是思考,不去考虑重复的矩形不计数进来
我们发现,当且仅当一个矩形a比矩形b的x要大,y却要小的时候,会出现非包含然后重叠出来一个新矩形的情况。
那么我们可以这样,对矩形按x轴排序,然后从小到大,比如枚举到第i个矩形,那么前1 - i-1个矩形必定x比他小,看看几个y比它大就能新生成几个。
但是要是你暴力这么做还是O(n ^ 2)的复杂度,怎么办?
我们可以离散化所有矩形的坐标,离散化可能有些同学不清楚,因为x和y很多,是10的9次,而n却很小,说明中间肯定很多坐标是空白没用的,比如线段【1,7】【2,4】你可以压缩成【1,4】【2,3】
这样的好处是,数据范围变成了n以内,然后线段之间的包含啊,相交关系是不变的(自己想想)显然,这个结论可以推到***。
那么离散了做什么呢?我们建一个前缀和数组ai,表示y为1 - i的矩形有多少。边枚举边更新。
那么有小伙伴问了,你这样更新,比如a1+1,a2,a3,a4不也得+1,还是n的平方啊
这里考虑用数据结构优化,我这里是利用树状数组(线段树也可以)可以做到logn的时间内维护前缀和。
那么这样问题就解决了。
等等,还没有解决,还有重叠的情况怎么办!?
这里必须要考虑去重了,我们发现,第一,当一个矩形的存在x和它相同,y比它大,的矩形以及另一个y和它相同,x比它大的矩形,它就一定可以被这俩矩形构造出来,因此这样的矩形,我们可以考虑不计算,直接continue
另外,如果对于矩形a存在x相同,y更大的矩形b,a和b的重叠矩形肯定是a,因此维护树状数组的时候必须对合适的矩形去添加。(这里细节有点不好说清楚,大家多画画图,我这里纠结卡了20分钟)
然后就AC了
下面是代码:
#include <stdio.h> #include <vector> #include <algorithm> #include <string.h> #include <limits.h> #include <string> #include <iostream> #include <queue> #include <math.h> #include <map> #include <stack> #include <set> #include <list> #include <forward_list> //#define left (now<<1) //#define right ((now<<1)+1) //#define mid ((l + r) >> 1) //#define midmid ((r + mid) >> 1) #define LONG_LONG_MIN -9223372036854775808ll #define LONG_LONG_MAX 9223372036854775807ll using namespace std; typedef long long int ll; const int MAXN = 1e5 + 10; const int MOD = 1e9 + 7; struct point{ ll x,y; }; vector<point> a[4],b[4]; ll bit[MAXN],maxx[MAXN],maxy[MAXN]; int t,n; ll ans; ll lowbit(ll x){ return x & (-x); } void add(ll x,ll y){ while(x <= 100000){ bit[x] += y; x += lowbit(x); } } ll getsum(ll x){ ll re = 0; while(x > 0){ re += bit[x]; x -= lowbit(x); } return re; } bool cmpy(point x,point y){ return x.y < y.y; } bool cmpx(point x,point y){ if(x.x != y.x){ return x.x < y.x; } return x.y < y.y; } int main(){ scanf("%d",&t); while(t--){ scanf("%d",&n); ans = 0; for(int i = 0; i < 4; ++i){ a[i].clear(); b[i].clear();} for(int i = 1; i <= n; ++i){ ll x,y; scanf("%lld%lld",&x,&y); ll p = 0; if(x < 0 && y > 0){ p = 1;} if(x < 0 && y < 0){ p = 2;} if(x > 0 && y < 0){ p = 3;} point pp; pp.x = abs(x); pp.y = abs(y); a[p].push_back(pp); } for(int i = 0; i < 4; ++i){ sort(a[i].begin(),a[i].end(),cmpy); //离散y ll cnt = 0; for(int j = 0; j < a[i].size(); ++j){ b[i].push_back(a[i][j]); if(j != 0 && a[i][j].y == a[i][j - 1].y){ b[i][j].y = b[i][j - 1].y; }else{ b[i][j].y = ++cnt; } } swap(a[i],b[i]); b[i].clear(); sort(a[i].begin(),a[i].end(),cmpx); //离散x cnt = 0; for(int j = 0; j < a[i].size(); ++j){ b[i].push_back(a[i][j]); if(j != 0 && a[i][j].x == a[i][j - 1].x){ b[i][j].x = b[i][j - 1].x; }else{ b[i][j].x = ++cnt; } } memset(bit,0,sizeof(bit)); memset(maxx,0,sizeof(maxx)); memset(maxy,0,sizeof(maxy)); for(int j = 0; j < b[i].size(); ++j){ point now = b[i][j]; maxx[now.y] = max(maxx[now.y],now.x); maxy[now.x] = max(maxy[now.x],now.y); } for(int j = 0; j < b[i].size(); ++j){ if(j != 0 && b[i][j].x == b[i][j - 1].x && b[i][j].y == b[i][j - 1].y){ continue;} if(maxx[b[i][j].y] > b[i][j].x && maxy[b[i][j].x] > b[i][j].y){ continue;} ++ans; //printf("%lld\n",getsum(100000)); if(maxx[b[i][j].y] == b[i][j].x){ ans = ans + getsum(100000) - getsum(b[i][j].y); } if(j < b[i].size() - 1 && maxy[b[i][j].x] == b[i][j].y){ add(b[i][j].y,1ll); } } } printf("%lld\n",ans); } }