华为OD机试 - 云服务安全策略最优选择 - 深度优先搜索DFS(Python/JS/C/C++/Go 新系统 200分)

Pandas入门教程(二十二):股票数据分析实战——yfinance 获取数据、技术指标与收益率分析 把 Pandas 用在真实场景,才能体会它的价值。本文以茅台股票为例,用 yfinance 获取历史行情,完成缺失值处理与列筛选,绘制收盘价曲线,计算 SMA 移动平均线与 RSI 相对强弱指数,并通过日收益率、累计收益率和波动率完成完整的量化分析流程。 阅读详情

在这里插入图片描述

华为OD机试 新系统 统一考试题库清单(持续收录中)以及考点说明(Python/JS/C/C++)。

专栏导读

本专栏收录于《华为OD机试真题(Python/JS/C/C++/Go)》。

刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新。

一、题目描述

在云服务中,有 n 个安全策略,编号 1 到 n。每个策略有一个重要度权重(正整数)。某些策略对之间互斥,不能同时启用。
现在需要为某个实例恰好选择 k 个策略,要求:

  1. 选中的策略之间没有互斥关系(即构成一个独立集);

  2. 在满足条件1的所有大小为 k 的策略组合中,使得选中策略的权重之和最大(权重之和就是重要度权重数组中的权重的和)。
    请返回所有满足上述条件的最优策略组合(即权重和最大的所有合法组合)。

二、输入描述

• 第一行:整数 n(策略总数)

• 第二行:整数 k(需要选择的策略数量)

• 第三行:长度为 n 的整数数组 weights,其中 weights[i] 表示策略 i + 1 的权重,字符串表示通过 , 分割

• 第四行:二维整数数组 conflicts,每个元素为 [a, b],表示策略 a 和 b 互斥(无向,无重复边),通过字符串表示,a和b之间通过 , 分割,不同组通过空格分割

数据规模:

• 1 ≤ n ≤ 25

• 0 ≤ k ≤ n

• 1 ≤ weights[i] ≤ 1000

• 0 ≤ conflicts.length ≤ n(n − 1)/2

三、输出描述

• 每个组合内的策略编号按升序排列;

• 所有组合按字典序排列(将每个组合视为一个数字序列);

• 如果没有合法组合(例如不存在大小为 k 的独立集),则返回空数组 []。

注意

• 如果没有大小为 k 的独立集,则返回 空

四、测试用例

测试用例1:

1、输入

4
2
5,1,3,4
1,2 2,3

2、输出

1,4

3、说明

合法的两策略组合中,1,4 的权重为 5+4=9,为唯一最大值。

测试用例2:

1、输入

5
3
3,4,3,4,3
1,3 2,4 3,5

2、输出

1,2,5 1,4,5

3、说明

1,2,5 和 1,4,5 均不存在内部冲突,权重都为 10,并且已经按照字典序输出。

五、解题思路

把 n 个安全策略看成图中的 n 个顶点,互斥关系看成无向边,题目实际要求寻找“大小恰好为 k 的最大权独立集”,并输出所有权重和相同的最优方案。

由于 n 最大只有 25,可以采用 DFS 回溯枚举组合。为了快速判断新选择的策略是否与已经选择的策略冲突,使用一个整数位掩码表示当前已选择策略,同时为每个策略维护 conflictMask:若 conflictMask[i] 的第 j 位为 1,说明策略 i 与策略 j 互斥。判断能否选择策略 i 只需执行 conflictMask[i] & selectedMask,复杂度为 O(1)。

单纯回溯最坏情况下需要枚举大量组合,因此增加一个 DP 乐观上界 upper[i][c]:表示从策略 i 到 n-1 中暂时忽略互斥关系、恰好再选 c 个策略时能够获得的最大权重。忽略冲突后的结果一定不小于真实可行结果,因此如果“当前权重 + upper”仍小于已经找到的最优权重,该分支一定不可能产生最优解,可以安全剪枝。

算法执行两遍 DFS。第一遍只计算最大权重 bestSum,不保存方案;第二遍再次按策略编号从小到大搜索,只输出权重恰好等于 bestSum 的方案。由于 DFS 始终按编号递增选择,所以每个组合内部天然升序,所有组合也天然按照字典序输出,无需额外排序。

预处理 upper 的复杂度为 O(nk),搜索最坏仍可能达到 O(2^n),这是一般图最大权独立集问题本身的性质;但 n≤25,并结合选取数量限制、冲突判断和上界剪枝后具有较好的实际性能。除递归路径和 upper 外不保存全部答案,额外空间约为 O(nk+n)。

六、Python算法源码

import sys

lines = sys.stdin.read().splitlines()

n = int(lines[0].strip())
k = int(lines[1].strip())

weights = list(map(int, lines[2].strip().split(',')))

conflict_line = lines[3].strip() if len(lines) > 3 else ''

# conflict_mask[i] 的第 j 位为 1,
# 表示策略 i 与策略 j 存在互斥关系。
conflict_mask = [0] * n

if conflict_line:
    for pair in conflict_line.split():
        a, b = map(int, pair.split(','))

        a -= 1
        b -= 1

        # 互斥关系为无向关系,两个方向均需要记录
        conflict_mask[a] |= 1 << b
        conflict_mask[b] |= 1 << a


NEG = -10 ** 30

# upper[i][need]:
# 从 i 到 n-1 中忽略互斥限制,恰好选 need 个策略的最大权重。
upper = [[NEG] * (k + 1) for _ in range(n + 1)]

upper[n][0] = 0

# 使用类似 0/1 背包的方式计算 DFS 的乐观上界
for i in range(n - 1, -1, -1):
    upper[i][0] = 0

    for need in range(1, k + 1):
        # 不选择当前策略
        skip = upper[i + 1][need]

        # 选择当前策略
        if upper[i + 1][need - 1] == NEG:
            take = NEG
        else:
            take = weights[i] + upper[i + 1][need - 1]

        upper[i][need] = max(skip, take)


best_sum = NEG


def dfs_best(start, need, selected_mask, total):
    """
    第一遍 DFS:
    只寻找最大权重,不保存具体组合。
    """
    global best_sum

    if need == 0:
        best_sum = max(best_sum, total)
        return

    # 剩余策略数量不足
    if n - start < need:
        return

    # 即使忽略所有冲突也无法达到当前最优值,可以安全剪枝
    if (
        upper[start][need] == NEG
        or (
            best_sum != NEG
            and total + upper[start][need] < best_sum
        )
    ):
        return

    for i in range(start, n - need + 1):

        # 位与非 0,说明当前策略与已选择策略存在冲突
        if conflict_mask[i] & selected_mask:
            continue

        dfs_best(
            i + 1,
            need - 1,
            selected_mask | (1 << i),
            total + weights[i]
        )


dfs_best(0, k, 0, 0)

if best_sum == NEG or k == 0:
    print('[]')
    sys.exit(0)


path = []
first_output = True
out = sys.stdout


def dfs_print(start, need, selected_mask, total):
    """
    第二遍 DFS:
    只输出权重等于 best_sum 的组合。
    """
    global first_output

    if need == 0:
        if total == best_sum:
            if not first_output:
                out.write(' ')

            first_output = False
            out.write(','.join(map(str, path)))

        return

    if n - start < need:
        return

    # 理论最大值都无法达到 best_sum,直接剪枝
    if (
        upper[start][need] == NEG
        or total + upper[start][need] < best_sum
    ):
        return

    # 从小到大选择策略,因此结果天然满足字典序
    for i in range(start, n - need + 1):

        if conflict_mask[i] & selected_mask:
            continue

        path.append(i + 1)

        dfs_print(
            i + 1,
            need - 1,
            selected_mask | (1 << i),
            total + weights[i]
        )

        path.pop()


dfs_print(0, k, 0, 0)
out.write('\n')

七、JavaScript算法源码

const fs = require('fs');

const lines = fs
    .readFileSync(0, 'utf8')
    .split(/\r?\n/);

const n = Number(lines[0].trim());
const k = Number(lines[1].trim());

const weights = lines[2]
    .trim()
    .split(',')
    .map(Number);

const conflictLine = (lines[3] || '').trim();

/*
 * n <= 25。
 * JavaScript 位运算使用 32 位整数,
 * bit 0 ~ bit 24 足够表示全部策略。
 */
const conflictMask = new Array(n).fill(0);

if (conflictLine.length > 0) {
    for (const pair of conflictLine.split(/\s+/)) {
        let [a, b] = pair.split(',').map(Number);

        a--;
        b--;

        // 互斥关系是无向的
        conflictMask[a] |= (1 << b);
        conflictMask[b] |= (1 << a);
    }
}


const NEG = Number.NEGATIVE_INFINITY;

/*
 * upper[i][need]:
 * 忽略冲突时,从 i 开始恰好选择 need 个策略
 * 可以获得的最大权重。
 */
const upper = Array.from(
    { length: n + 1 },
    () => new Array(k + 1).fill(NEG)
);

upper[n][0] = 0;


// 0/1 DP 计算乐观上界
for (let i = n - 1; i >= 0; i--) {
    upper[i][0] = 0;

    for (let need = 1; need <= k; need++) {

        const skip = upper[i + 1][need];

        const take =
            upper[i + 1][need - 1] === NEG
                ? NEG
                : weights[i]
                + upper[i + 1][need - 1];

        upper[i][need] = Math.max(skip, take);
    }
}


let bestSum = NEG;


/*
 * 第一遍 DFS:
 * 只求最大权重 bestSum。
 */
function dfsBest(start, need, selectedMask, sum) {

    if (need === 0) {
        bestSum = Math.max(bestSum, sum);
        return;
    }

    if (n - start < need) {
        return;
    }

    /*
     * 忽略冲突之后的理论最大值仍低于 bestSum,
     * 当前分支一定不可能成为最优解。
     */
    if (
        upper[start][need] === NEG
        || (
            bestSum !== NEG
            && sum + upper[start][need] < bestSum
        )
    ) {
        return;
    }

    for (let i = start; i <= n - need; i++) {

        // 与已经选择的策略存在冲突
        if ((conflictMask[i] & selectedMask) !== 0) {
            continue;
        }

        dfsBest(
            i + 1,
            need - 1,
            selectedMask | (1 << i),
            sum + weights[i]
        );
    }
}


dfsBest(0, k, 0, 0);


if (bestSum === NEG || k === 0) {
    console.log('[]');
    process.exit(0);
}


const path = [];

let firstOutput = true;


/*
 * 第二遍 DFS:
 * 只输出权重恰好等于 bestSum 的方案。
 */
function dfsPrint(start, need, selectedMask, sum) {

    if (need === 0) {

        if (sum === bestSum) {

            if (!firstOutput) {
                process.stdout.write(' ');
            }

            firstOutput = false;

            process.stdout.write(
                path.join(',')
            );
        }

        return;
    }

    if (n - start < need) {
        return;
    }

    // 理论上界无法达到 bestSum
    if (
        upper[start][need] === NEG
        || sum + upper[start][need] < bestSum
    ) {
        return;
    }

    /*
     * 按策略编号从小到大枚举,
     * 因而无需额外进行字典序排序。
     */
    for (let i = start; i <= n - need; i++) {

        if ((conflictMask[i] & selectedMask) !== 0) {
            continue;
        }

        path.push(i + 1);

        dfsPrint(
            i + 1,
            need - 1,
            selectedMask | (1 << i),
            sum + weights[i]
        );

        path.pop();
    }
}


dfsPrint(0, k, 0, 0);

process.stdout.write('\n');

八、C算法源码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <limits.h>

#define MAXN 25
#define LINE_SIZE 8192

int n, k;

int weights[MAXN];

/*
 * conflictMask[i]:
 * 用二进制位保存与策略 i 互斥的所有策略。
 */
uint32_t conflictMask[MAXN];

long long upper[MAXN + 1][MAXN + 1];

long long bestSum;

int path[MAXN];
int pathLen = 0;

int firstOutput = 1;

const long long NEG = LLONG_MIN / 4;


/*
 * 第一遍 DFS:
 * 只计算最大的权重和。
 */
void dfsBest(
    int start,
    int need,
    uint32_t selectedMask,
    long long sum
) {

    if (need == 0) {
        if (sum > bestSum) {
            bestSum = sum;
        }

        return;
    }

    // 剩余策略不够选择
    if (n - start < need) {
        return;
    }

    /*
     * upper 是忽略冲突后的乐观上界。
     * 如果连理论最大值都达不到 bestSum,
     * 当前分支可以直接舍弃。
     */
    if (
        upper[start][need] == NEG
        || (
            bestSum != NEG
            && sum + upper[start][need] < bestSum
        )
    ) {
        return;
    }

    for (int i = start; i <= n - need; ++i) {

        /*
         * 位与结果非 0,
         * 表示策略 i 与某个已选策略互斥。
         */
        if ((conflictMask[i] & selectedMask) != 0) {
            continue;
        }

        dfsBest(
            i + 1,
            need - 1,
            selectedMask | (1u << i),
            sum + weights[i]
        );
    }
}


/*
 * 第二遍 DFS:
 * 输出所有权重等于 bestSum 的组合。
 */
void dfsPrint(
    int start,
    int need,
    uint32_t selectedMask,
    long long sum
) {

    if (need == 0) {

        if (sum == bestSum) {

            if (!firstOutput) {
                putchar(' ');
            }

            firstOutput = 0;

            for (int i = 0; i < pathLen; ++i) {

                if (i > 0) {
                    putchar(',');
                }

                printf("%d", path[i]);
            }
        }

        return;
    }

    if (n - start < need) {
        return;
    }

    /*
     * 理论上限都不足 bestSum,
     * 说明这个分支不可能产生最优方案。
     */
    if (
        upper[start][need] == NEG
        || sum + upper[start][need] < bestSum
    ) {
        return;
    }

    /*
     * 按编号递增回溯,
     * 因此最终输出天然满足字典序。
     */
    for (int i = start; i <= n - need; ++i) {

        if ((conflictMask[i] & selectedMask) != 0) {
            continue;
        }

        path[pathLen++] = i + 1;

        dfsPrint(
            i + 1,
            need - 1,
            selectedMask | (1u << i),
            sum + weights[i]
        );

        --pathLen;
    }
}


int main(void) {

    char line[LINE_SIZE];


    // 第一行:n
    fgets(line, sizeof(line), stdin);
    n = atoi(line);


    // 第二行:k
    fgets(line, sizeof(line), stdin);
    k = atoi(line);


    // 第三行:weights
    fgets(line, sizeof(line), stdin);

    char *token = strtok(
        line,
        ",\r\n"
    );

    for (
        int i = 0;
        i < n && token != NULL;
        ++i
    ) {

        weights[i] = atoi(token);

        token = strtok(
            NULL,
            ",\r\n"
        );
    }


    memset(
        conflictMask,
        0,
        sizeof(conflictMask)
    );


    /*
     * 第四行 conflicts 可能为空。
     */
    line[0] = '\0';

    if (
        fgets(
            line,
            sizeof(line),
            stdin
        ) != NULL
    ) {

        token = strtok(
            line,
            " \t\r\n"
        );

        while (token != NULL) {

            int a, b;

            if (
                sscanf(
                    token,
                    "%d,%d",
                    &a,
                    &b
                ) == 2
            ) {

                --a;
                --b;

                // 无向互斥关系
                conflictMask[a] |=
                    (1u << b);

                conflictMask[b] |=
                    (1u << a);
            }

            token = strtok(
                NULL,
                " \t\r\n"
            );
        }
    }


    /*
     * 初始化 upper。
     */
    for (int i = 0; i <= n; ++i) {

        for (int c = 0; c <= k; ++c) {

            upper[i][c] = NEG;
        }
    }

    upper[n][0] = 0;


    /*
     * 0/1 DP:
     * 忽略冲突,求后缀恰好选择 need 个策略时
     * 最大能够获得的权重。
     */
    for (int i = n - 1; i >= 0; --i) {

        upper[i][0] = 0;

        for (
            int need = 1;
            need <= k;
            ++need
        ) {

            long long skip =
                upper[i + 1][need];

            long long take;

            if (
                upper[i + 1][need - 1]
                == NEG
            ) {
                take = NEG;
            } else {
                take =
                    weights[i]
                    + upper[i + 1][need - 1];
            }

            upper[i][need] =
                skip > take
                    ? skip
                    : take;
        }
    }


    bestSum = NEG;


    // 第一遍求最大权重
    dfsBest(
        0,
        k,
        0u,
        0
    );


    if (
        bestSum == NEG
        || k == 0
    ) {

        printf("[]\n");

        return 0;
    }


    // 第二遍输出全部最优组合
    dfsPrint(
        0,
        k,
        0u,
        0
    );

    putchar('\n');

    return 0;
}

九、C++算法源码

#include <bits/stdc++.h>

using namespace std;

int n, k;

vector<int> weights;

/*
 * conflictMask[i] 的二进制位表示
 * 与策略 i 互斥的策略集合。
 */
vector<uint32_t> conflictMask;

vector<vector<long long>> upper;

long long bestSum;

const long long NEG = LLONG_MIN / 4;

vector<int> path;

bool firstOutput = true;


/*
 * 第一遍 DFS,只求最大权重。
 */
void dfsBest(
    int start,
    int need,
    uint32_t selectedMask,
    long long sum
) {

    if (need == 0) {
        bestSum = max(
            bestSum,
            sum
        );

        return;
    }

    if (n - start < need) {
        return;
    }

    /*
     * 忽略冲突后的理论最大值仍然小于当前最优值,
     * 则该分支没有继续搜索的必要。
     */
    if (
        upper[start][need] == NEG
        || (
            bestSum != NEG
            && sum + upper[start][need]
               < bestSum
        )
    ) {
        return;
    }

    for (
        int i = start;
        i <= n - need;
        ++i
    ) {

        // 当前策略与已选择策略存在互斥
        if (
            (conflictMask[i] & selectedMask)
            != 0
        ) {
            continue;
        }

        dfsBest(
            i + 1,
            need - 1,
            selectedMask | (1u << i),
            sum + weights[i]
        );
    }
}


/*
 * 第二遍 DFS,只输出达到 bestSum 的组合。
 */
void dfsPrint(
    int start,
    int need,
    uint32_t selectedMask,
    long long sum
) {

    if (need == 0) {

        if (sum == bestSum) {

            if (!firstOutput) {
                cout << ' ';
            }

            firstOutput = false;

            for (
                int i = 0;
                i < (int) path.size();
                ++i
            ) {

                if (i > 0) {
                    cout << ',';
                }

                cout << path[i];
            }
        }

        return;
    }

    if (n - start < need) {
        return;
    }

    /*
     * 第二遍已经知道目标 bestSum,
     * 连理论上限都达不到目标时直接剪枝。
     */
    if (
        upper[start][need] == NEG
        || sum + upper[start][need]
           < bestSum
    ) {
        return;
    }

    /*
     * 策略始终按编号升序加入 path,
     * 所有答案天然满足题目的字典序要求。
     */
    for (
        int i = start;
        i <= n - need;
        ++i
    ) {

        if (
            (conflictMask[i] & selectedMask)
            != 0
        ) {
            continue;
        }

        path.push_back(i + 1);

        dfsPrint(
            i + 1,
            need - 1,
            selectedMask | (1u << i),
            sum + weights[i]
        );

        path.pop_back();
    }
}


int main() {

    ios::sync_with_stdio(false);
    cin.tie(nullptr);


    string line;


    // n
    getline(cin, line);
    n = stoi(line);


    // k
    getline(cin, line);
    k = stoi(line);


    // weights
    getline(cin, line);

    weights.resize(n);

    stringstream ws(line);

    string part;

    for (int i = 0; i < n; ++i) {

        getline(
            ws,
            part,
            ','
        );

        weights[i] = stoi(part);
    }


    conflictMask.assign(
        n,
        0
    );


    // conflicts 这一行可能为空
    if (
        getline(cin, line)
        && !line.empty()
    ) {

        stringstream cs(line);

        string pair;

        while (cs >> pair) {

            size_t pos =
                pair.find(',');

            int a =
                stoi(
                    pair.substr(
                        0,
                        pos
                    )
                ) - 1;

            int b =
                stoi(
                    pair.substr(
                        pos + 1
                    )
                ) - 1;

            // 无向互斥关系
            conflictMask[a] |=
                (1u << b);

            conflictMask[b] |=
                (1u << a);
        }
    }


    /*
     * 初始化 upper。
     */
    upper.assign(
        n + 1,
        vector<long long>(
            k + 1,
            NEG
        )
    );

    upper[n][0] = 0;


    /*
     * 预处理忽略冲突情况下的最大后缀权重,
     * 作为回溯过程中的乐观上界。
     */
    for (int i = n - 1; i >= 0; --i) {

        upper[i][0] = 0;

        for (
            int need = 1;
            need <= k;
            ++need
        ) {

            long long skip =
                upper[i + 1][need];

            long long take =
                upper[i + 1][need - 1]
                        == NEG
                    ? NEG
                    : weights[i]
                      + upper[i + 1][need - 1];

            upper[i][need] =
                max(
                    skip,
                    take
                );
        }
    }


    bestSum = NEG;


    // 第一遍:计算最大权重
    dfsBest(
        0,
        k,
        0u,
        0
    );


    if (
        bestSum == NEG
        || k == 0
    ) {

        cout << "[]\n";

        return 0;
    }


    // 第二遍:输出全部最优组合
    dfsPrint(
        0,
        k,
        0u,
        0
    );


    cout << '\n';

    return 0;
}

十、Go算法源码

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

const NEG int64 = -1 << 60

var (
	n int
	k int

	weights []int64

	// conflictMask[i] 使用二进制位保存
	// 与策略 i 存在互斥关系的所有策略。
	conflictMask []uint32

	// upper[i][need] 是忽略冲突后的乐观权重上界。
	upper [][]int64

	bestSum int64 = NEG

	path []int

	firstOutput = true

	writer *bufio.Writer
)

/*
第一遍 DFS:
只寻找最大权重 bestSum。
*/
func dfsBest(
	start int,
	need int,
	selectedMask uint32,
	sum int64,
) {

	if need == 0 {
		if sum > bestSum {
			bestSum = sum
		}

		return
	}

	// 剩余策略数量不足
	if n-start < need {
		return
	}

	/*
		upper 忽略了所有互斥关系,
		因此是当前分支可能取得权重的乐观上界。

		如果连理论最大值都低于 bestSum,
		该分支必然无法产生更优方案。
	*/
	if upper[start][need] == NEG ||
		(bestSum != NEG &&
			sum+upper[start][need] < bestSum) {

		return
	}

	for i := start; i <= n-need; i++ {

		/*
			位与非 0,
			说明当前策略与某个已选择策略存在冲突。
		*/
		if conflictMask[i]&selectedMask != 0 {
			continue
		}

		dfsBest(
			i+1,
			need-1,
			selectedMask|(uint32(1)<<i),
			sum+weights[i],
		)
	}
}

/*
第二遍 DFS:
只输出权重等于 bestSum 的组合。
*/
func dfsPrint(
	start int,
	need int,
	selectedMask uint32,
	sum int64,
) {

	if need == 0 {

		if sum == bestSum {

			if !firstOutput {
				fmt.Fprint(
					writer,
					" ",
				)
			}

			firstOutput = false

			for i, v := range path {

				if i > 0 {
					fmt.Fprint(
						writer,
						",",
					)
				}

				fmt.Fprint(
					writer,
					v,
				)
			}
		}

		return
	}

	if n-start < need {
		return
	}

	/*
		第二遍已经知道最终目标 bestSum,
		理论上界都无法达到 bestSum 时直接剪枝。
	*/
	if upper[start][need] == NEG ||
		sum+upper[start][need] < bestSum {

		return
	}

	/*
		按策略编号递增选择,
		因此组合内部和组合之间都天然满足字典序。
	*/
	for i := start; i <= n-need; i++ {

		if conflictMask[i]&selectedMask != 0 {
			continue
		}

		path = append(
			path,
			i+1,
		)

		dfsPrint(
			i+1,
			need-1,
			selectedMask|(uint32(1)<<i),
			sum+weights[i],
		)

		// 回溯,删除刚加入的策略
		path = path[:len(path)-1]
	}
}

func main() {

	scanner :=
		bufio.NewScanner(os.Stdin)


	// 第一行 n
	scanner.Scan()

	n, _ =
		strconv.Atoi(
			strings.TrimSpace(
				scanner.Text(),
			),
		)


	// 第二行 k
	scanner.Scan()

	k, _ =
		strconv.Atoi(
			strings.TrimSpace(
				scanner.Text(),
			),
		)


	// 第三行 weights
	scanner.Scan()

	weightParts :=
		strings.Split(
			strings.TrimSpace(
				scanner.Text(),
			),
			",",
		)

	weights =
		make(
			[]int64,
			n,
		)

	for i := 0; i < n; i++ {

		v, _ :=
			strconv.Atoi(
				strings.TrimSpace(
					weightParts[i],
				),
			)

		weights[i] =
			int64(v)
	}


	/*
		解析互斥关系。
		第四行允许为空。
	*/
	conflictMask =
		make(
			[]uint32,
			n,
		)

	conflictLine := ""

	if scanner.Scan() {
		conflictLine =
			strings.TrimSpace(
				scanner.Text(),
			)
	}

	if conflictLine != "" {

		for _, pair :=
			range strings.Fields(
				conflictLine,
			) {

			ab :=
				strings.Split(
					pair,
					",",
				)

			a, _ :=
				strconv.Atoi(
					strings.TrimSpace(
						ab[0],
					),
				)

			b, _ :=
				strconv.Atoi(
					strings.TrimSpace(
						ab[1],
					),
				)

			a--
			b--

			// 互斥关系是无向的
			conflictMask[a] |=
				uint32(1) << b

			conflictMask[b] |=
				uint32(1) << a
		}
	}


	/*
		初始化 upper 数组。
	*/
	upper =
		make(
			[][]int64,
			n+1,
		)

	for i := range upper {

		upper[i] =
			make(
				[]int64,
				k+1,
			)

		for c := 0; c <= k; c++ {
			upper[i][c] = NEG
		}
	}

	upper[n][0] = 0


	/*
		类似 0/1 背包:

		忽略策略之间的冲突,
		计算从后缀中恰好选择 need 个策略时
		最大的权重和。

		该值用于 DFS 的安全剪枝。
	*/
	for i := n - 1; i >= 0; i-- {

		upper[i][0] = 0

		for need := 1; need <= k; need++ {

			skip :=
				upper[i+1][need]

			take := NEG

			if upper[i+1][need-1] != NEG {

				take =
					weights[i] +
						upper[i+1][need-1]
			}

			if skip > take {

				upper[i][need] =
					skip

			} else {

				upper[i][need] =
					take
			}
		}
	}


	// 第一遍:求最大权重
	dfsBest(
		0,
		k,
		0,
		0,
	)


	writer =
		bufio.NewWriter(
			os.Stdout,
		)

	defer writer.Flush()


	if bestSum == NEG || k == 0 {

		fmt.Fprintln(
			writer,
			"[]",
		)

		return
	}


	// 第二遍:输出全部最优组合
	dfsPrint(
		0,
		k,
		0,
		0,
	)

	fmt.Fprintln(writer)
}


🏆下一篇:华为OD机试真题 - 简易内存池(Python/JS/C/C++ 新系统 200分)

🏆本文收录于,华为OD机试真题(Python/JS/C/C++/Go)

刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新。

在这里插入图片描述

2026华为OD机考新系统真题 + 双机位C卷 + 往年真题 - 题库目录分值说明 + 考点说明+在线OJ 2026年4月1日起,华为OD机试正式切换至新系统模式!采用力扣模式,无需自己写输入! 难度大幅度降低。正在备考华为OD机试的同学,优先刷新系统机试真题,可大幅提升通过率。 新系统考题考点与旧版一致,无需重新适应。本专栏所有算法题均配有详细思路解析,并提供C语言、JS、C++、Python、Go、Java多语言源码实现。订阅后私信即可开通专属OJ刷题权限,高效备考一次上岸! 阅读详情

相关推荐

【零基础学智能仿真-68】本地仿真助手的工具调用

本节学习把本地大模型的计算建议转化为可检查的工具调用。以两单元均匀杆为例,建立工具白名单、参数范围和原需求一致性校验,完成有限元求解、物理验收、结果回传与日志保存。通过合法调用、未知工具和参数篡改案例理解失败处理,并提供可选的Ollama接入程序,区分模型解释与工具原始结果,为下一节综合仿真研究打通计算入口。

2402_84593233的博客 324

Python+Agent入门笔记(二):列表推导式、模块和异常,这些写 Agent 前得会

接上一篇《Python+Agent入门笔记(一)》。基础语法跑通之后,这篇聊几块日常写脚本绕不开的东西。还是按 JS/Go 开发者的视角写,尽量记录我实际卡过的地方。

qq_41241612的博客 419

深入解析LCD DRM驱动框架中的KMS与GEM机制

本文深入解析了Linux内核中DRM(Direct Rendering Manager)驱动框架的核心机制,重点剖析了KMS(内核模式设置)与GEM(图形执行管理器)的工作原理。KMS作为显示系统的总指挥,负责原子化地控制CRTC、Encoder、Plane等硬件资源,确保画面同步更新,避免撕裂;GEM则作为显存大管家,通过DUMB和PRIME机制高效管理图形缓冲区的分配、共享与同步。文章结合RK3568平台实战,阐述了如何利用这些机制优化显示性能,并提供了调试技巧与常见问题解决方案。

weixin_29188997的博客 386

详解Python条件语句

在 Python 中,if...else 是一种条件语句,可以根据给定的条件执行不同的操作。这个语句通常用于控制程序的流程。

2601_96798737的博客 199

linux虚拟机和电脑ping通(可上网)

linux虚拟机(Ubuntu)和电脑ping通。

观则明的博客 4114

TP4054充电电路发热严重?手把手教你调整电阻值降低温度(附实测数据)

本文针对TP4054锂电池充电电路发热严重的问题,提供了详细的排错与解决方案。通过分析发热原理,指出核心在于PROG引脚电阻值设定,并手把手指导如何计算和选择正确的电阻以降低充电电流与温度。文末附有不同电阻下的实测温度与性能数据,为电子爱好者和工程师提供了实用的调试参考。

p4q5r6s7t的博客 405

微调数据集构建:清洗、标注、格式转换完整流程

摘要解决微调数据直接抓取导致的噪声污染、模式崩溃与Token错位问题。适用于需为大模型SFT阶段构建高质量训练集的数据与算法工程师。读完可获得包含启发式清洗、MinHash去重及ChatML格式对齐的完整可运行数据流水线。关键词数据清洗、MinHash去重、数据标注、ChatML、SFT数据集、数据配比背景与问题大模型微调效果的上限由数据质量决定,直接使用未经处理的原始语料极易引发模型复读、幻觉与指令遵循能力退化。

2402_85729970的博客 462

深度学习(Deep Learning, DL)简介

深度学习(Deep Learning, DL)是机器学习的一个子领域,主要使用多层神经网络来处理复杂的数据和任务。深度学习通过模拟人脑的神经网络结构,能够自动提取特征并进行高效的模式识别和预测。

qq_28791753的博客 1万+

Python 正则表达式:5 个日常够用的模式

第一段</p><p>第二段</p>'],一次吞到了最后一个 </p>;非贪婪结果是 ['<p>第一段</p>', '<p>第二段</p>'],每段分开取出。输出说明:第一行是 ['20260115', '299', '3'];第二行是 ('2026', '01', '15'),三个分组分别对应年、月、日。输出说明:结果是 ['苹果', '香蕉', '橙子', '葡萄', '西瓜']。后,就会尽量少匹配。html = '<p>第一段</p><p>第二段

云物缭绕 215

Pi0 项目复现及项目问题解决方案(全网抢先发布~)

Pi0 项目复现及项目问题解决方案(全网抢先发布~)

m0_68167062的博客 2260

【RL in VLA】VLA中的强化学习

本文提出了一种名为 Reinforcement Learning Distilled Generalist(RLDG) 的方法,通过强化学习生成高质量训练数据来微调机器人基础模型。通过在多个精确操作任务上的实验,我们证明了 RLDG 在成功率和泛化能力上显著优于使用人类演示的传统微调方法。RLDG 不仅减少了对人类演示数据的依赖,还展示了在复杂多阶段任务中的灵活性和有效性。

三木今天学习了嘛の博客 2256

Python 基础系列教程(二):运算符与字符串详解

本文详解Python运算符与字符串核心知识。涵盖七类运算符(算术、赋值、比较、逻辑等)及其优先级,重点解析/与//、负数整除、取余符号规则及幂运算右结合特性。通过实例揭示运算符的类型多态性,如字符串乘法、列表拼接。深入讲解字符串索引、切片、不可变性及常用方法,并介绍三种格式化方式。附带海象运算符:=等进阶用法,助力夯实编程基础。

810cheng 473

如何在 Ubuntu 22.04 上安装和配置 VNC 服务器?

VNC(Virtual Network Computing)是一种图形桌面共享系统,通过网络允许用户控制另一台计算机的桌面环境。无论你是系统管理员还是开发人员,VNC 都可以在远程访问桌面环境时提供极大的便利。在安装和配置 VNC 服务器之前,我们需要确保系统是最新的,并安装一些基本工具。这些命令将更新你的软件包列表并升级所有已安装的软件包,以确保你拥有最新的安全修复和功能更新。

网络技术联盟站 4001

Python3 协程:异步编程的核心机制与实战

协程是解决I/O密集型任务等待瓶颈的高效机制,相比线程更轻量、无锁开销。从yield到async/await的演进,使异步编程语义清晰、易于理解。通过async/await可实现并发执行多个任务(如gather、create_task),显著提升网络请求、爬虫等场景性能。关键在于使用异步库(如aiohttp)替代同步库,避免阻塞事件循环。合理控制超时与并发数,才能发挥协程最大优势。

Lxn2zh的博客 149

【PCIe 总线及设备入门学习专栏 6.1 -- PCIe MCTP】

MCTP 是由 DMTF(Distributed Management Task Force)定义的协议,用于提供设备和主机间基于不同传输层(如 PCIe、I²C、USB 等)的管理消息通信能力。在 PCIe 环境下,MCTP 消息作为的一种特殊形式传输,并被赋予唯一的 VDM Code 值0000b,以区别于其他 VDM。MCTP 在 PCIe 环境中作为一种基于 Type 1 VDM 的管理通信协议,提供了设备与主机之间的灵活数据传输能力。

CodingCos的博客 2125

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

python如何控制浏览器

Python 可以通过 Selenium、Playwright 等工具实现浏览器自动化,从打开网页到执行复杂交互操作均可脚本化完成。Selenium生态成熟

2601_96797912的博客 64

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

torch.compile 差异仅 1e-6,边界样本预测为何翻转

torch.compile 加速后输出与 eager 不一致,部分预测翻转。逐层对比与 graph break 分析定位到动态分支翻转根因。容差回归仅适用于无分支翻转场景,零容忍场景应禁用编译。

昨日种种,皆成今我。今日种种,皆成新我。 152

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

Python 100讲课程导航:第08篇《Python input 与类型转换:构建可靠的交互程序》

本文详解Python中input()的返回类型为字符串,强调必须通过int()、float()等函数进行类型转换。重点讲解了输入验证的重要性,结合try-except处理ValueError等异常,实现可靠的交互程序。通过购物预算计算器实战,演示如何防范非法输入、处理边界值,并推荐使用Decimal避免浮点精度问题,提升程序健壮性。

铭渊老黄 126

Day07 Python数据存储与运算实战全解

本文是《AI应用开发筑基》系列的第一篇技术笔记,聚焦Python编程中最底层的两块基石——**数据存储**(字面量、变量、五大基本类型、内存模型)与**数据运算**(四种运算符、I/O交互、类型转换机制)。全文采用"概念解析→源码演示→输出验证→踩坑总结"四步分析法,逐层拆解每个知识点的底层逻辑。文末附ATM取款机完整实战,带你从零写出第一个交互式Python程序。适合零基础入门及需要系统梳理Python基础的技术人员阅读。

2301_79028807的博客 229

python自动化框架中的类,函数,库,模块,文件,包,项目名等等有没有命名规范

python自动化框架中的类,函数,库,模块,文件,包,项目名等等有没有命名规范

simpleness_的博客 331

飞凌嵌入式ElfBoard-数据类型-元组

my_tuple = (1, 2, 3, 'a', 'b', 'c') # my_tuple 包含了多个元素,包括整数和字符串。print(my_tuple[1:4]) # 输出:(2, 3, 'a'),切片访问从索引1到索引3(不包括索引4)的元素。my_tuple = (1, 2, 3, 'a', 'b', 'c') # 再次定义 my_tuple,内容与之前相同。print(my_tuple[3:]) # 输出:('a', 'b', 'c'),切片访问从索引3到结束的元素。

2301_76587520的博客 160

python之用scapy分层解析pcap报文(Ethernet帧、IP数据包、TCP数据包、UDP数据包、Raw数据包)

文章浏览阅读2.6w次,点赞21次,收藏136次。本文介绍了如何利用Python的scapy库进行网络报文的分层解析,包括从pcap文件中读取数据、实时抓包

2601_96798011的博客 231

【Python】国家中小学智慧教育平台电子教材批量下载与 PDF 合成实战

成果:两本语文电子课本(一年级上册 118 页 / 23MB、三年级上册 130 页 / 30.3MB),合计 248 页、约 54MB,单本下载加合成 40~60 秒,PDF 页数校验全部通过。经验教训SPA 站点的数据接口优先从 bundle 里挖,比抓页面稳得多;资源链接要按 private/public 分组看待,只取平台公开部分;页数、目录名这类“可推断”的信息,永远用实际响应验证一次;受限环境里“能不能跑浏览器”要在方案定稿前确认。后续计划:这版 PDF 是图片版,没有文字层;

木头大左的博客 92

魔塔社区微调训练Qwen2-1.5B-Instruct模型并烧录到‌RK3568开发板教程

本文基于Ubuntu 22.04 + CUDA 13.0.3环境,使用LLaMA-Factory框架对Qwen2-1.5B-Instruct模型进行LoRA微调,训练儿童故事生成专用模型。通过配置qwen模板、制作alpaca格式数据集、执行sft训练并合并LoRA权重,最终将模型转换为GGUF格式,并量化为Q4_K_M以适配RK3568嵌入式平台。全程支持国产镜像加速下载与显存优化,实现高效轻量化部署。

博客 173

【回眸】DeepSeek V4.1 Flash 快速上手与实战

本文介绍如何高效本地部署开源大模型,通过现代化工具链实现分钟级环境搭建。聚焦显存优化、一键安装、代码调用与多轮对话实现,解决“显存不足”“连接超时”等痛点,提供参数调优与结构化输出技巧,助力开发者在离线场景下快速构建稳定、高效的本地推理系统。

啤酒鸭的物联网开发记录博客 182

利用python中matplotlib.pyplot库绘制图

plt.plot(x,y) # x,y是每个点的坐标,plot()把每个点按顺序画折线。plt.subplots(2,2)表示绘制2*2的子图布局,axs[0,0]表示在第0行第0列绘制子图...coordinates_i[:,0]表示获取每一行(每一个spot)的第0列(x坐标)coordinates_i[:,1]表示获取每一行(每一个spot)的第0列(y坐标)plot.subplot(2,2,1) 表示子图布局是2*2的,开始绘制第一个。也可以导入数据绘制。可以设置color属性,设置颜色。

qq_46147236的博客 88
上一篇: 华为OD机试 - 云服务安全策略最优选择 - 深度优先搜索DFS(Java 新系统 200分)
下一篇: 华为OD机试 - 数据中心最佳维护窗口 - 前缀和 + 单调双端队列(Java 新系统 200分)
哪 吒
哪 吒 2022年度博客之星TOP 1 2022年度博客之星TOP 1 领域专家: 后端开发技术领域 领域专家: 后端开发技术领域
博客等级 码龄9年 59万+粉丝 2933原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哪 吒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值