24年春招/暑期实习-阿里-笔试真题卷(3)

第一题:打游戏

在线测评链接:http://121.196.235.151/p/P1145

题目描述

ak机正在玩一个游戏,游戏中 n n n个怪物,血量上限分别为 h i h_i hi,初始时所有怪物的血量都等于它们的血量上限,当怪物的血量小于或等于0时,怪物将会死亡。
ak机有两个技能
第一个技能为旋风斩,消耗一点法力,对所有怪物造成1点伤害
第二个技能为斩杀,消耗两点法力,杀死一个已受伤的怪物(当前怪物血量小于怪物的血量上限)。
两个技能都没有使用次数限制,ak机想知道她最少需要消耗多少点法力才能杀死所有怪物。

输入描述

第一行输入一个整数 n ( 2 ≤ n ≤ 1 0 5 ) n(2 ≤n≤ 10^5) n(2n105)表示怪物数量。

第二行输入 n n n个整数 h i ( 1 ≤ h i ≤ 1 0 9 ) h_i(1 ≤ h_i≤ 10^9) hi(1hi109)表示怪物的血量上限

输出描述

输出一个整数表示答案,

样例

输入

3
1 1 4

输出

3

说明

首先使用旋风斩,怪物的血量变成:0 0 3,第1、2个怪物死亡。
再对第三个怪物使用斩杀,第3个怪物死亡。消耗的法力值为1+2=3。

思路:模拟 枚举

首先,根据题目描述,一技能一定要至少释放一次(这是释放二技能的前提)

首先一种暴力的思想是可以直接枚举使用一技能的次数 c n t cnt cnt

但是,由于怪物的血量范围是 1 ≤ h i ≤ 1 0 9 1\le h_i\le 10^9 1hi109,因此我们肯定不能直接枚举 c n t cnt cnt

由于只有 n ( 1 ≤ n ≤ 1 0 5 ) n(1\le n\le 10^5) n(1n105)个怪物,因此我们可以对怪物的血量从小到大进行排序,然后枚举第 i i i个怪物被一技能直接干掉

i i i个怪物被一技能直接干掉的一技能使用次数即为 w [ i ] w[i] w[i],显然前面 i − 1 i-1 i1个怪物也会被干掉

对于后面的怪物,都是用第二个技能,即为第 i i i个怪物的最小花费

因此第 i i i个怪物的最小花费即为 w [ i ] + ( n − i − 1 ) × 2 w[i]+(n-i-1)\times 2 w[i]+(ni1)×2

C++

#include<bits/stdc++.h>
using namespace std;
const int N=1E5+10;
int w[N],n;
int main(){
    cin>>n;
    for(int i=0;i<n;i++)cin>>w[i];
    sort(w,w+n);
    int res=1e9;
    for(int i=0;i<n;i++){
        res=min(res,w[i]+(n-i-1)*2);
    }
    cout<<res<<endl;
    return 0;
}

Java

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        Integer[] w = new Integer[n];
        for(int i = 0; i < n; i++) {
            w[i] = scanner.nextInt();
        }
        Arrays.sort(w);
        int res = (int)1e9;
        for(int i = 0; i < n; i++) {
            res = Math.min(res, w[i] + (n - i - 1) * 2);
        }
        System.out.println(res);
    }
}

Python

n = int(input())
w = list(map(int, input().split()))
w.sort()
res = int(1e9)
for i in range(n):
    res = min(res, w[i] + (n - i - 1) * 2)
print(res)

第二题:互补数组

在线测评链接:http://121.196.235.151/p/P1146

题目描述

ak机定义两个数组是互补的,当且仅当数组每一个位置的数字之和都相同。

ak机有两个长度为 n n n的数组,分别是 a a a b b b,她想知道有多少个子序列对应的数组是互补的。

输入描述

第一行输入一个整数 n ( 1 ≤ n ≤ 1 0 5 ) n(1 ≤n≤ 10^5) n(1n105)表示数组长度

第二行输入 n n n个整数表示数组 a ( 1 ≤ a i ≤ 1 0 9 ) a(1≤ a_i≤ 10^9) a(1ai109)

第三行输入 m m m个整数表示数组 b ( 1 ≤ b i ≤ 1 0 9 ) b(1 ≤b_i\le 10^9) b(1bi109)

输出描述

输出一个整数,由于这个整数可能很大,因此你需要输出这个整数对 1 0 9 + 7 10^9 +7 109+7取模后的结果。

样例

输入

3
1 2 3
3 2 1

输出

7

说明

子序列:1,2,3,12,13,23,123,都满足条件
这里的1,2,3指的是数组下标的位置(从1开始)

思路:哈希表+贡献法计数+快速幂

首先解释一下样例1,因为任意一个位置 i i i a [ i ] + b [ i ] a[i]+b[i] a[i]+b[i]都为4,因此有3个4,我们可以考虑选或者不选(至少要选1个位置)

因此,总的情况就是 2 3 − 1 = 7 2^3-1=7 231=7

我们可以使用一个哈希表来统计所有位置 a [ i ] + b [ i ] a[i]+b[i] a[i]+b[i]出现的次数,对于某一个权值 w w w,其出现的次数为 c n t cnt cnt

那么它对答案的贡献就是 2 c n t − 1 2^{cnt}-1 2cnt1

注意由于答案过大,需要对 1 0 9 + 7 10^9+7 109+7取模,因此这里我们可以使用快速幂来快速地求出来 2 k 2^k 2k,更新答案

本题数据范围较小,因此这里就不给大家使用快速幂的做法了,我们使用另一种做法,维护一个数组来预处理 2 k 2^k 2k的结果

C++

#include<bits/stdc++.h>
using namespace std;
const int N=1E5+10,mod=1e9+7;
int a[N],b[N],n,res;
int main(){
    cin>>n;
    for(int i=0;i<n;i++)cin>>a[i];
    for(int i=0;i<n;i++)cin>>b[i];
    unordered_map<int,int>cnts;
    for(int i=0;i<n;i++){
        cnts[a[i]+b[i]]++;
    }
    vector<int>twos(n+1,1);
    for(int i=1;i<=n;i++){
        twos[i]=1ll*twos[i-1]*2%mod;
    }
    for(auto &[u,v]:cnts){
        res=(res+1ll*(twos[v]-1+mod)%mod)%mod;
    }
    cout<<res<<endl;
    return 0;
}

Java

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int[] a = new int[n];
        int[] b = new int[n];
        for(int i = 0; i < n; i++) {
            a[i] = scanner.nextInt();
        }
        for(int i = 0; i < n; i++) {
            b[i] = scanner.nextInt();
        }
        HashMap<Integer, Integer> cnts = new HashMap<>();
        for(int i = 0; i < n; i++) {
            cnts.put(a[i] + b[i], cnts.getOrDefault(a[i] + b[i], 0) + 1);
        }
        int[] twos = new int[n + 1];
        twos[0] = 1;
        int mod = (int)1e9 + 7;
        for(int i = 1; i <= n; i++) {
            twos[i] = (int)(((long)twos[i - 1] * 2) % mod);
        }
        int res = 0;
        for(int v : cnts.values()) {
            res = (res + ((twos[v] - 1 + mod) % mod)) % mod;
        }
        System.out.println(res);
    }
}

Python

n = int(input())
a = list(map(int, input().split()))
b = list(map(int, input().split()))
cnts = {}
for i in range(n):
    cnts[a[i] + b[i]] = cnts.get(a[i] + b[i], 0) + 1
twos = [1] * (n + 1)
mod = int(1e9) + 7
for i in range(1, n + 1):
    twos[i] = twos[i - 1] * 2 % mod
res = 0
for v in cnts.values():
    res = (res + (twos[v] - 1 + mod) % mod) % mod
print(res)

第三题:数的最大权值

在线测评链接:http://121.196.235.151/p/P1107

题目描述

小苯定义一个数字的权值为:该数字的因子个数。

小苯现在拿到了一个正整数,他希望将:分解为若干不等于1的数字(也可以不做分解),使得所有分解出的正整数乘积等于 x x x,且所有数字的权值之和尽可能大,你能帮帮他求出最大的权值吗。

输入描述

输入包含 T 十 1 T 十1 T1行。
第一行一个正整数 T ( 1 ≤ T ≤ 1 0 4 ) T(1\le T\le 10^4) T(1T104),表示数据组数。

接下来 T T T行,每行一个正整数 x ( 2 ≤ x ≤ 1 0 5 ) x(2 \le x \le10^5) x(2x105),表示每组数据中小苯询问的数字 x x x

输出描述

输出包含 T T T 行,每行一个正整数表示每组测试数据的最大权值和

样例

输入

3
2
10
123

输出

2
4
4

说明

第一个测试数据中,无法分解,直接取2的权值为 2.
第二个测试数据中,将 10 分解为 2x5,权值和为 4。

思路:质因数分解+分类讨论

本题有两种情况

  • 情况1: x x x仅有一个质因子 a a a,即 x = a k x=a^k x=ak

考虑两种情况,拆分和不拆分

拆分:对于每一个质因子,它的因子个数都是2(1和它本身),因此对应的答案为 2 k 2k 2k

不拆分:对应因子个数为 k + 1 k+1 k+1(有 1 , a , a 2 , . . . , a k 1,a,a^2,...,a^k 1,a,a2,...,ak这些因子,共计 k + 1 k+1 k+1个)

只要 k ≥ 1 k\ge 1 k1,一定有 2 k ≥ k + 1 2k\ge k+1 2kk+1,因此拆分是最优解

例如8,只有一个质因子2,不拆分的话就只有1 2 4 8这4个因子,拆了就是3个2,共计6个因子,因此拆分比不拆分的因子个数要多。

  • 情况2: x x x有多个质因子,即 x = a 1 k 1 × a 2 k 2 × . . . a m k m x=a_1^{k_1}\times a_2^{k_2}\times ...a_m^{k_m} x=a1k1×a2k2×...amkm

拆分:根据情况1的计算公式,权值总和为 ∑ i = 1 m ( k i + 1 ) \sum_{i=1}^{m}(k_i+1) i=1m(ki+1)

不拆分:根据情况1的计算公式,权值总和为 ∏ i = 1 m ( y i + 1 ) \prod _{i=1}^{m}(y_i+1) i=1m(yi+1)

我们可以明显得知,当 a , b ≥ 2 时 a,b\ge 2时 a,b2 a × b ≥ a + b a\times b\ge a+b a×ba+b

因此选择不拆分是最优解

例如30,有质因子2,3,5,如果不拆分的话有1,2,3,5,6,10,15,30这8个因子,如果拆分的话,就是1个2,1个3,1个5这6个因子,因此不拆分要比拆分的因子个数要多。

C++

#include<bits/stdc++.h>
using namespace std;
const int N=1E5+10,mod=1e9+7;
int T,n;
long long solve(int x){
    vector<pair<int,int>>v;  //记录质因子和其出现的次数
    for(int i=2;i*i<=x;i++){
        if(x%i==0){
            int cnt=0;
            while(x%i==0){
                cnt++;
                x/=i;
            }
            v.push_back({i,cnt});
        }
    }
    if(x>1){
        v.push_back({x,1});
    }
    if(v.size()==1)return v[0].second*2;  //只有一个质因子
    long long res=1;
    for(auto &t:v){
        res*=(t.second+1);
    }
    return res;
}
int main(){
    cin>>T;
    while(T--){
        cin>>n;
        cout<<solve(n)<<endl;
    }
    return 0;
}

Java

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int T = scanner.nextInt();
        while(T-- > 0) {
            int n = scanner.nextInt();
            System.out.println(solve(n));
        }
    }

    private static long solve(int x) {
        List<Pair<Integer, Integer>> v = new ArrayList<>();  // 记录质因子和其出现的次数
        for(int i = 2; i * i <= x; i++) {
            if(x % i == 0) {
                int cnt = 0;
                while(x % i == 0) {
                    cnt++;
                    x /= i;
                }
                v.add(new Pair<>(i, cnt));
            }
        }
        if(x > 1) {
            v.add(new Pair<>(x, 1));
        }
        if(v.size() == 1) return v.get(0).second * 2;  // 只有一个质因子
        long res = 1;
        for(Pair<Integer, Integer> t : v) {
            res *= (t.second + 1);
        }
        return res;
    }

    static class Pair<T, U> {
        T first;
        U second;

        Pair(T first, U second) {
            this.first = first;
            this.second = second;
        }
    }
}

Python

T = int(input())
def solve(x):
    v = []  # 记录质因子和其出现的次数
    i = 2
    while i * i <= x:
        if x % i:
            i += 1
        else:
            cnt = 0
            while x % i == 0:
                cnt += 1
                x //= i
            v.append((i, cnt))
    if x > 1:
        v.append((x, 1))
    if len(v) == 1: return v[0][1] * 2  # 只有一个质因子
    res = 1
    for t in v:
        res *= (t[1] + 1)
    return res

for _ in range(T):
    n = int(input())
    print(solve(n))
  • 20
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值