阿里算法岗 0530笔试真题 - 寻找满足条件的最优子序列

寻找满足条件的最优子序列

阿里算法岗 0530笔试 第三题

题目内容

给定长度为 nnn 的数组 aaa,小C想选出一个长度为 kkk 的子序列。

所选子序列需满足相邻两个元素的 gcdgcdgcd 不为 111。具体地,若选取下标序列 i1<i2<⋯<iki_1 < i_2 < \dots < i_ki1<i2<⋯<ik,则需要满足:gcd(aij,aij+1)>1gcd(a_{i_j}, a_{i_{j+1}}) > 1gcd(aij,aij+1)>1(1≤j<k1 \le j < k1≤j<k)。

在所有满足上述条件的子序列中,求最大元素的最小可能取值。

如果不存在满足条件的子序列,则输出 −1-1−1。

【名词解释】

  • 子序列:从原序列中删除任意个(可以为零)元素得到的新序列。
  • gcdgcdgcd:即最大公因数,指两个整数共有约数中最大的一个。例如,gcd(12,30)=6gcd(12, 30) = 6gcd(12,30)=6。

输入描述

第一行输入两个整数 n,kn, kn,k(1≤k≤n1 \le k \le n1≤k≤n;2≤n≤2×1052 \le n \le 2 \times 10^52≤n≤2×105),分别表示数组长度和所选子序列长度。

第二行输入 nnn 个整数 a1,a2,...,ana_1, a_2, \dots, a_na1,a2,...,an(1≤ai≤1091 \le a_i \le 10^91≤ai≤109),表示数组元素的值。

输出描述

输出一个整数,表示满足条件的子序列中,最大元素的最小可能取值。

如果不存在满足条件的子序列,则输出 −1-1−1。

样例1

输入

复制代码
5 3
2 4 3 9 6

输出

复制代码
6

说明

  • 可选下标 1,2,51, 2, 51,2,5 对应元素 {2,4,6}\{2, 4, 6\}{2,4,6},此时 gcd(2,4)=2>1gcd(2, 4) = 2 > 1gcd(2,4)=2>1,gcd(4,6)=2>1gcd(4, 6) = 2 > 1gcd(4,6)=2>1,最大值为 666。
  • 可知没有更小的最大值,因此输出 666。

样例2

输入

复制代码
5 2
5 7 11 13 17

输出

复制代码
-1

说明

在这个样例中,数组中任意两个元素的 gcdgcdgcd 均为 111,无法选出符合条件的子序列,因此输出 −1-1−1。

题解

思路

二分 + dp

  1. 预处理每个数的不同质因子,可以先使用质数筛求出值域中所有质数,在求出每个数的不同质因子。
  2. 根据a的最大值和最小值确定二分的边界left = min(a), right = max(a)
  3. 二分枚举确定结果,每次枚举mid = (left + right) / 2进行check(mid), 直到left > right结束
    1. true: 更新ans = mid, right = mid - 1
    2. false: 更新left = mid + 1
  4. check函数的作用为只允许使用 a[i] <= mid 的数,是否能够选出一个长度至少为 k 的子序列,使得相邻元素 gcd > 1。 使用dp进行求解
    • 定义best,含义为当前处理到的位置之前,以某个含有质因子p的数结尾的最长合法子序列长度。
    • 状态转移:
      • 对于当前数a[i], 它的质因子是p1,p2.p3..., 同时gcd(x,y) > 1 => x 和y有公共质因子,所有当前数可以接在任何含有这些质因子的链后面。加入当前数之后能够组成最长链长度就为dp = max(best[p] + 1)
      • 对当前数的所有质因子都更新best[p] = dp
    • 按照上述逻辑如果存在best§ >= k 说明成立返回true,否则返回false

C++

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int n, k;
vector<int> a;

// 预筛质数
vector<int> primes;
// 每个位置对应的不同质因子
vector<vector<int>> factors;
// 质数筛
void initPrimes() {
    const int LIM  = 31623; // 大于sqrt(10^9) 的最小整数;
    vector<bool> isPrime(LIM + 1, true);
    isPrime[0] = isPrime[1] = false;
    for (int i = 2; i * i <= LIM; i++) {
        if (isPrime[i]) {
            for (int j = i * i; j <= LIM; j += i) {
                isPrime[j] = false;
            }
        }
    }
    
    for (int i = 2; i <= LIM; i++) {
        if (isPrime[i]) {
            primes.push_back(i);
        }
    }
}

// 找出所有数的质因子
void buildFactors() {
    factors.resize(n);
    for (int i = 0; i < n; i++) {
        int x = a[i];
        for (int p : primes) {
            if (1LL * p * p > x) {
                break;
            }
            if (x % p == 0) {
                factors[i].push_back(p);
                while (x % p == 0) {
                    x /= p;
                }
            }
        }
        // 由于 p * p 退出
        if (x > 1) {
            factors[i].push_back(x);
        }
    }
}

// 检验mid是否可行
bool check(int mid) {
    // 以p为结尾最大链
    vector<int> best(31624, 0);
    int longest = 0;
    
    for (int i = 0; i < n; i++) {
        if (a[i] > mid) {
            continue;
        }
        
        int dp = 1;
        for (int p : factors[i]) {
            dp = max(dp, best[p] + 1);
        }
        

        for (int p : factors[i]) {
            best[p] = dp;
        }
        
        longest = max(longest, dp);
        if (longest >= k) {
            return true;
        }
    }
    return false;
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
   
    cin >> n >> k;
    a.resize(n);
    
    int right = 0;
    int left = INT_MAX;
    
    for (int i = 0; i < n; i++) {
        cin >> a[i];
        right = max(right, a[i]);
        left = min(left, a[i]);
    }
    
    // 特判1
    if (k == 1) {
        cout << left << endl;
        return 0;
    }
    initPrimes();
    buildFactors();
    int ans = -1;
    while (left <= right) {
        int mid = (left + right) >> 1;
        if (check(mid)) {
            ans = mid;
            right = mid - 1;
        } else {
            left = mid + 1;
        }
    }
    
    cout << ans;
    return 0;
}

java

java 复制代码
import java.io.*;
import java.util.*;

public class Main {

    static int n, k;
    static int[] a;
    static List<Integer> primes = new ArrayList<>();
    static List<List<Integer>> factors;

    // 质数筛
    static void initPrimes() {
        int LIM = 31623;
        boolean[] isPrime = new boolean[LIM + 1];
        Arrays.fill(isPrime, true);
        isPrime[0] = isPrime[1] = false;

        for (int i = 2; i * i <= LIM; i++) {
            if (isPrime[i]) {
                for (int j = i * i; j <= LIM; j += i) {
                    isPrime[j] = false;
                }
            }
        }

        for (int i = 2; i <= LIM; i++) {
            if (isPrime[i]) primes.add(i);
        }
    }

    // 分解质因子
    static void buildFactors() {
        factors = new ArrayList<>();
        for (int i = 0; i < n; i++) factors.add(new ArrayList<>());

        for (int i = 0; i < n; i++) {
            int x = a[i];
            for (int p : primes) {
                if (1L * p * p > x) break;
                if (x % p == 0) {
                    factors.get(i).add(p);
                    while (x % p == 0) x /= p;
                }
            }
            if (x > 1) factors.get(i).add(x);
        }
    }

    static boolean check(int mid) {
        int[] best = new int[31624];
        int longest = 0;

        for (int i = 0; i < n; i++) {
            if (a[i] > mid) continue;

            int dp = 1;

            for (int p : factors.get(i)) {
                dp = Math.max(dp, best[p] + 1);
            }

            for (int p : factors.get(i)) {
                best[p] = dp;
            }

            longest = Math.max(longest, dp);
            if (longest >= k) return true;
        }
        return false;
    }

    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        String[] first = br.readLine().split(" ");
        n = Integer.parseInt(first[0]);
        k = Integer.parseInt(first[1]);

        a = new int[n];

        String[] arr = br.readLine().split(" ");
        int left = Integer.MAX_VALUE, right = 0;

        for (int i = 0; i < n; i++) {
            a[i] = Integer.parseInt(arr[i]);
            left = Math.min(left, a[i]);
            right = Math.max(right, a[i]);
        }

        if (k == 1) {
            System.out.println(left);
            return;
        }

        initPrimes();
        buildFactors();

        int ans = -1;
        while (left <= right) {
            int mid = (left + right) >> 1;
            if (check(mid)) {
                ans = mid;
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }

        System.out.println(ans);
    }
}

python

python 复制代码
import sys
input = sys.stdin.readline

n, k = map(int, input().split())
a = list(map(int, input().split()))

# 质数筛
LIM = 31623
is_prime = [True] * (LIM + 1)
is_prime[0] = is_prime[1] = False

p = 2
while p * p <= LIM:
    if is_prime[p]:
        for j in range(p * p, LIM + 1, p):
            is_prime[j] = False
    p += 1

primes = [i for i in range(2, LIM + 1) if is_prime[i]]

# 分解质因子
factors = [[] for _ in range(n)]

for i in range(n):
    x = a[i]
    for p in primes:
        if p * p > x:
            break
        if x % p == 0:
            factors[i].append(p)
            while x % p == 0:
                x //= p
    if x > 1:
        factors[i].append(x)

def check(mid):
    best = [0] * 31624
    longest = 0

    for i in range(n):
        if a[i] > mid:
            continue

        dp = 1
        for p in factors[i]:
            dp = max(dp, best[p] + 1)

        for p in factors[i]:
            best[p] = dp

        longest = max(longest, dp)
        if longest >= k:
            return True

    return False

left, right = min(a), max(a)

if k == 1:
    print(left)
    sys.exit()

ans = -1
while left <= right:
    mid = (left + right) // 2
    if check(mid):
        ans = mid
        right = mid - 1
    else:
        left = mid + 1

print(ans)

javascript

js 复制代码
const readline = require("readline");

const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

let lines = [];

rl.on("line", (line) => {
    lines.push(line.trim());
});

rl.on("close", () => {
    let [n, k] = lines[0].split(" ").map(Number);
    let a = lines[1].split(" ").map(Number);

    let left = Math.min(...a);
    let right = Math.max(...a);

    const LIM = 31623;

    // 质数筛
    let isPrime = new Array(LIM + 1).fill(true);
    isPrime[0] = isPrime[1] = false;

    for (let i = 2; i * i <= LIM; i++) {
        if (isPrime[i]) {
            for (let j = i * i; j <= LIM; j += i) {
                isPrime[j] = false;
            }
        }
    }

    let primes = [];
    for (let i = 2; i <= LIM; i++) {
        if (isPrime[i]) primes.push(i);
    }

    // 分解质因子
    let factors = Array.from({ length: n }, () => []);

    for (let i = 0; i < n; i++) {
        let x = a[i];

        for (let p of primes) {
            if (p * p > x) break;

            if (x % p === 0) {
                factors[i].push(p);
                while (x % p === 0) x /= p;
            }
        }

        if (x > 1) factors[i].push(x);
    }

    function check(mid) {
        let best = new Array(31624).fill(0);
        let longest = 0;

        for (let i = 0; i < n; i++) {
            if (a[i] > mid) continue;

            let dp = 1;

            for (let p of factors[i]) {
                dp = Math.max(dp, best[p] + 1);
            }

            for (let p of factors[i]) {
                best[p] = dp;
            }

            longest = Math.max(longest, dp);
            if (longest >= k) return true;
        }

        return false;
    }

    let ans = -1;

    while (left <= right) {
        let mid = (left + right) >> 1;

        if (check(mid)) {
            ans = mid;
            right = mid - 1;
        } else {
            left = mid + 1;
        }
    }

    console.log(ans);
});

Go

go 复制代码
package main

import (
    "bufio"
    "fmt"
    "os"
)

var n, k int
var a []int
var primes []int
var factors [][]int

func initPrimes() {
    LIM := 31623
    isPrime := make([]bool, LIM+1)
    for i := 2; i <= LIM; i++ {
        isPrime[i] = true
    }

    for i := 2; i*i <= LIM; i++ {
        if isPrime[i] {
            for j := i * i; j <= LIM; j += i {
                isPrime[j] = false
            }
        }
    }

    for i := 2; i <= LIM; i++ {
        if isPrime[i] {
            primes = append(primes, i)
        }
    }
}

func buildFactors() {
    factors = make([][]int, n)

    for i := 0; i < n; i++ {
        x := a[i]

        for _, p := range primes {
            if p*p > x {
                break
            }
            if x%p == 0 {
                factors[i] = append(factors[i], p)
                for x%p == 0 {
                    x /= p
                }
            }
        }

        if x > 1 {
            factors[i] = append(factors[i], x)
        }
    }
}

func check(mid int) bool {
    best := make([]int, 31624)
    longest := 0

    for i := 0; i < n; i++ {
        if a[i] > mid {
            continue
        }

        dp := 1

        for _, p := range factors[i] {
            if best[p]+1 > dp {
                dp = best[p] + 1
            }
        }

        for _, p := range factors[i] {
            best[p] = dp
        }

        if dp > longest {
            longest = dp
        }

        if longest >= k {
            return true
        }
    }

    return false
}

func main() {
    reader := bufio.NewReader(os.Stdin)

    fmt.Fscan(reader, &n, &k)

    a = make([]int, n)

    left := int(1<<31 - 1)
    right := 0

    for i := 0; i < n; i++ {
        fmt.Fscan(reader, &a[i])
        if a[i] < left {
            left = a[i]
        }
        if a[i] > right {
            right = a[i]
        }
    }

    if k == 1 {
        fmt.Println(left)
        return
    }

    initPrimes()
    buildFactors()

    ans := -1

    for left <= right {
        mid := (left + right) >> 1

        if check(mid) {
            ans = mid
            right = mid - 1
        } else {
            left = mid + 1
        }
    }

    fmt.Println(ans)
}
相关推荐
@小阿宝2 小时前
机器人正向逆向运动学
算法·机器人
小雨下雨的雨2 小时前
数独算法与求解器鸿蒙PC Electron框架完成深度解析
javascript·人工智能·算法·游戏·华为·electron·鸿蒙系统
HZ·湘怡2 小时前
数据结构之排序算法 (1)--插入排序
c语言·数据结构·算法·排序算法
ouliten2 小时前
[Triton笔记7]融合注意力 (Fused Attention)
人工智能·笔记·算法
开源Z2 小时前
LeetCode 238 · 除自身以外数组的乘积:左右两遍扫描,不用除法
算法·leetcode
BAGAE2 小时前
FEC-RS前向纠错编码理论及工程实施研究
c语言·c++·qt·算法·决策树·链表
兰令水2 小时前
leecodecode【状态机DP】【2026.6.9打卡-java版本】
java·开发语言·算法
8Qi82 小时前
LeetCode 5:最长回文子串(Longest Palindromic Substring)—— 题解
算法·leetcode·职场和发展·动态规划
j7~2 小时前
【算法】专题一:双指针之移动零,复写零,快乐数
数据结构·c++·算法·双指针·快乐数·移动零·复写零