题目链接2021阿里巴巴校招笔试真题_Java工程师、C++工程师_牛客网 (nowcoder.com)
目录
第一题
思路:
先对物品进行排序,根据x的从小到大,y从大到小的规则,y从大到小是为了防止最长上升子序列中有x相同的情况
求y的最长上升子序列长度,用lower_bound函数,贪心+二分思想
#include <algorithm>
#include <bits/stdc++.h>
using namespace std;
typedef struct {
int a;
int b;
} pii;
pii s1[100100];
int d[100100], n;
const int INF = 0x3f3f3f3f;
bool cmp(pii& a, pii& b) {
if (a.a != b.a) return a.a < b.a;
return a.b > b.b;
}
int get_result(pii s[]) {
sort(s + 1, s + 1 + n, cmp);
fill(d, d + n + 1, INF);
int result = 1;
for (int i = 1; i <= n; i++) {
int j = lower_bound(d, d + n + 1, s[i].b) - d;
result = max(result, j + 1);
d[j] = s[i].b;
}
if (result == 1) return 0;
else return result;
}
int main() {
int t;
cin >> t;
while (t--) {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> s1[i].a;
}
for (int i = 1; i <= n; i++) {
cin >> s1[i].b;
}
printf("%lld\n", get_result(s1));
}
}
第二题
思路:
线性dp,先推出公式
x+y=A xy=B
dp[n]=A*dp[n-1]-B-dp[n-2]
#include <iostream>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
ll dp[100010];
int main() {
int t;
cin>>t;
while (t--) { // 注意 while 处理多个 case
int a,b,n;
cin>>a>>b>>n;
dp[1]=a;
dp[2]=a*a-2*b;
for(int i=3;i<=n;i++)
dp[i]=((a*dp[i-1])%mod-(b*dp[i-2])%mod+mod)%mod;
cout<<dp[n]<<"\n";
}
}
第三题
思路:
树形dp,一颗节点数为n的数的方案数是由其根节点的左右子树方案数决定的,还需要考虑树的高度,因此dp要考虑第二维高度
dp[i][j]表示结点个数为i,高度小于等于j的方案数
推导公式则为dp[i][j]+=dp[i-k-1][j-1]*dp[k][j-1] 0<=k<=i-1
#include <iostream>
using namespace std;
typedef long long ll;
const ll mod = 1e9+7;
ll dp[55][55];
int main() {
int m,n;
cin>>n>>m;
for(int i=0;i<=m;i++)
dp[0][i]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
for(int k=0;k<=i-1;k++)
{
dp[i][j]=(dp[i][j]+(dp[i-k-1][j-1]*dp[k][j-1])%mod)%mod;
}
}
cout<<dp[n][m];
}
第四题
思路:
一眼BFS,dp[i][j]表示走到i,j坐标的最小步数,但是队列里得包含对称飞行器的使用次数
#include <iostream>
#include <queue>
using namespace std;
typedef struct {
int x, y, p;
} pii;
const int INF = 0x3f3f3f3f;
int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};
int dp[510][510];
queue<pii> q;
string s[510];
int main() {
int n, m, sx, sy, fx, fy;
cin >> n >> m;
for (int i = 0; i < n; i++)
cin >> s[i];
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++) {
dp[i][j] = INF;
if (s[i][j] == 'S') {
sx = i;
sy = j;
}
if (s[i][j] == 'E') {
fx = i;
fy = j;
}
}
dp[sx][sy] = 0;
q.push({sx, sy, 5});
while (!q.empty()) {
pii tmp = q.front();
q.pop();
int x = tmp.x, y = tmp.y, p = tmp.p;
for (int i = 0; i < 4; i++) {
int xx = x + dx[i];
int yy = y + dy[i];
if (xx < 0 || xx >= n || yy < 0 || yy >= m || s[xx][yy]=='#') continue;
if (dp[xx][yy] > dp[x][y] + 1) {
dp[xx][yy] = dp[x][y] + 1;
q.push({xx, yy, p});
}
}
int syn_x = n - 1 - x, syn_y = m - 1 - y;
if (dp[syn_x][syn_y] > dp[x][y] + 1 && p > 0 && s[syn_x][syn_y]!='#') {
dp[syn_x][syn_y] = dp[x][y] + 1;
q.push({syn_x, syn_y, p - 1});
}
}
if (dp[fx][fy] == INF) cout << -1;
else cout << dp[fx][fy];
}
// 64 位输出请用 printf("%lld")
第五题
(个人认为前五题最难的一道,前面四道还能做出来,这道只能看题解了,还是自己太菜了哇)
思路:
首先想到一点,一个人的abs(a,b)越大,代表如果要这个人合作的话,最小能力值是由这个人决定,可以理解为木桶效应(什么奇怪比喻?)
先把利用abs(a,b)从小到大排序
假设一个人i的能力值ai<bi,那么合作的总能力值就是a
假设合作的人能力值是aj,bj
ai+aj<bi+bj
得
ai-bi<bj-aj
由于abs(aj-bj)<abs(ai-bi) 并且 ai<bi 所以上面不等式成立
所以只要再维护两个a和b的前缀最大值数组就可以了
#include <algorithm>
#include <cmath>
#include <iostream>
using namespace std;
typedef struct{
int a,b,p;
}piii;
piii s[200010];
int pre_a[200010],pre_b[200010];
bool cmp(piii &a,piii &b)
{
return a.p<b.p;
}
int main() {
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
int a,b;
cin>>a>>b;
s[i].a=a;
s[i].b=b;
s[i].p=abs(a-b);
}
sort(s+1, s+1+n, cmp);
for(int i=1;i<=n;i++)
{
pre_a[i]=max(pre_a[i-1],s[i].a);
pre_b[i]=max(pre_b[i-1],s[i].b);
}
int result=0;
for(int i=1;i<=n;i++)
{
result=max(result,s[i].a<s[i].b?s[i].a+pre_a[i-1]:pre_b[i-1]+s[i].b);
}
printf("%.1f",result/2.0);
}
// 64 位输出请用 printf("%lld")
总结:题目好难,后面五道题不想做了::>_<::,这就是大厂的含金量吗