题目链接
题目描述
给定 个
维的特征向量
,以及每个位置
的容量
。你需要选择一系列“注意力”连边
(其中
),使得每个位置
接收的连边不超过
条,并且所有选定连边的信息总量
最大。
信息总量的计算步骤如下:
- RMSNorm 归一化:对每个向量
进行归一化得到
。
- 贡献度计算:每条连边
的贡献度为
,总信息量
是所有选定连边的贡献度平方之和,即
。
- 目标:最大化
,并输出
round(100 * S)。
解题思路
这是一个结合了向量运算和贪心策略的模拟题。题目的关键在于正确理解计算流程,并根据示例反推出一个与描述有出入的关键公式。
1. RMS 归一化
首先,按照题目定义对每个输入向量 进行归一化。对于向量
:
- 计算各分量平方的均值:
mean_sq = (x_1^2 + ... + x_d^2) / d - 计算
rms = sqrt(mean_sq) - 归一化向量
的每个分量为原分量除以
rms。
2. 修正贡献度公式
题目描述中,贡献度 被简单地描述为归一化后向量的点积。然而,如果我们直接使用
来计算,其结果与示例给出的中间值和最终答案均不相符。
通过对示例数据进行反向推导,可以发现实际的贡献度公式为:
即点积之后,还需要除以维度
的平方根。后续的计算必须基于这个修正后的公式。
3. 贪心选择
题目明确指出:“对每个 的选择彼此独立...贪心即最优”。这为我们提供了清晰的解题路径:
- 我们可以对每个接收位置
(从
到
)进行独立的决策。
- 对于一个固定的
,我们计算所有可能的来源位置
(其中
)到它的贡献度平方,即
。
- 将所有这些
的值存入一个列表。
- 对该列表进行降序排序。
- 从排序后的列表中,选取前
个最大的值(如果列表长度小于
,则全选)。
- 将这
个值累加到总信息量
中。
4. 汇总与输出
- 遍历完所有
后,我们就得到了最大的总信息量
。
- 最后,根据题目要求,计算
round(100 * S)并输出。round为标准四舍五入。
代码
#include <iostream>
#include <vector>
#include <cmath>
#include <numeric>
#include <algorithm>
#include <iomanip>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(NULL);
int n, d;
cin >> n >> d;
vector<vector<double>> vectors(n, vector<double>(d));
for (int i = 0; i < n; ++i) {
for (int j = 0; j < d; ++j) {
cin >> vectors[i][j];
}
}
vector<int> capacities(n);
for (int i = 0; i < n; ++i) {
cin >> capacities[i];
}
// 1. RMSNorm 归一化
vector<vector<double>> normalized_vectors(n, vector<double>(d));
for (int i = 0; i < n; ++i) {
double sum_sq = 0.0;
for (int j = 0; j < d; ++j) {
sum_sq += vectors[i][j] * vectors[i][j];
}
double rms = sqrt(sum_sq / d);
for (int j = 0; j < d; ++j) {
normalized_vectors[i][j] = vectors[i][j] / rms;
}
}
double total_s = 0.0;
double sqrt_d = sqrt(d);
// 2. 贪心选择
for (int j = 1; j < n; ++j) {
if (capacities[j] == 0) {
continue;
}
vector<double> contributions_sq;
for (int i = 0; i < j; ++i) {
double dot_product = 0.0;
for (int k = 0; k < d; ++k) {
dot_product += normalized_vectors[i][k] * normalized_vectors[j][k];
}
double a_ij = dot_product / sqrt_d;
contributions_sq.push_back(a_ij * a_ij);
}
sort(contributions_sq.rbegin(), contributions_sq.rend());
for (int k = 0; k < min((int)contributions_sq.size(), capacities[j]); ++k) {
total_s += contributions_sq[k];
}
}
// 3. 输出
cout << llround(100 * total_s) << "\n";
return 0;
}
import java.util.Scanner;
import java.util.Vector;
import java.util.Collections;
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int d = sc.nextInt();
double[][] vectors = new double[n][d];
for (int i = 0; i < n; i++) {
for (int j = 0; j < d; j++) {
vectors[i][j] = sc.nextDouble();
}
}
int[] capacities = new int[n];
for (int i = 0; i < n; i++) {
capacities[i] = sc.nextInt();
}
// 1. RMSNorm 归一化
double[][] normalizedVectors = new double[n][d];
for (int i = 0; i < n; i++) {
double sumSq = 0.0;
for (int j = 0; j < d; j++) {
sumSq += vectors[i][j] * vectors[i][j];
}
double rms = Math.sqrt(sumSq / d);
for (int j = 0; j < d; j++) {
normalizedVectors[i][j] = vectors[i][j] / rms;
}
}
double totalS = 0.0;
double sqrtD = Math.sqrt(d);
// 2. 贪心选择
for (int j = 1; j < n; j++) {
if (capacities[j] == 0) {
continue;
}
ArrayList<Double> contributionsSq = new ArrayList<>();
for (int i = 0; i < j; i++) {
double dotProduct = 0.0;
for (int k = 0; k < d; k++) {
dotProduct += normalizedVectors[i][k] * normalizedVectors[j][k];
}
double a_ij = dotProduct / sqrtD;
contributionsSq.add(a_ij * a_ij);
}
contributionsSq.sort(Collections.reverseOrder());
for (int k = 0; k < Math.min(contributionsSq.size(), capacities[j]); k++) {
totalS += contributionsSq.get(k);
}
}
// 3. 输出
System.out.println(Math.round(100 * totalS));
}
}
import math
def main():
n, d = map(int, input().split())
vectors = []
for _ in range(n):
vectors.append(list(map(float, input().split())))
capacities = list(map(int, input().split()))
# 1. RMSNorm 归一化
normalized_vectors = []
for vec in vectors:
sum_sq = sum(x * x for x in vec)
rms = math.sqrt(sum_sq / d)
normalized_vectors.append([x / rms for x in vec])
total_s = 0.0
sqrt_d = math.sqrt(d)
# 2. 贪心选择
for j in range(1, n):
if capacities[j] == 0:
continue
contributions_sq = []
for i in range(j):
dot_product = sum(normalized_vectors[i][k] * normalized_vectors[j][k] for k in range(d))
a_ij = dot_product / sqrt_d
contributions_sq.append(a_ij * a_ij)
contributions_sq.sort(reverse=True)
num_to_take = min(len(contributions_sq), capacities[j])
total_s += sum(contributions_sq[:num_to_take])
# 3. 输出
# 标准四舍五入
print(int(100 * total_s + 0.5))
if __name__ == "__main__":
main()
算法及复杂度
- 算法:模拟、贪心
- 时间复杂度:
。主要瓶颈在于两层循环遍历所有可能的连边
,并在内层计算
维向量的点积。对于每个
,还需要
的时间进行排序,但这部分通常小于点积计算的总耗时。
- 空间复杂度:
,用于存储所有归一化后的向量。

京公网安备 11010502036488号