注意力调度问题

题意

给定长度为 的序列,每个位置 有一个 维特征向量 和一个容量 。先对每个向量做 RMSNorm 归一化,然后对任意 )计算贡献 。每个位置 最多选 条来自前序位置的入边,要求选边使得总贡献 最大,输出

思路

拆开来看,每个位置 的选边是独立的——位置 选谁做入边,不影响位置 )的可选集合。所以不存在全局竞争,每个位置各自贪心就行。

具体做法:

  1. 对每个向量做 RMSNorm:,归一化向量
  2. 对每个位置 ,枚举所有 ,算出贡献
  3. 把这些贡献从大到小排序,取前 个累加到
  4. 最后输出

为什么每个位置独立贪心就是最优?

关键在于:入边选择没有"排他性"。位置 被位置 选为入边,不妨碍位置 )也选 做入边。容量限制 只约束" 自己最多收几条边",不约束" 最多被引用几次"。所以每个 的决策互不干扰,各自选最大的 个贡献即可。

拿样例验证:,归一化后

各对贡献(除以 ):

容量

  • ,不选。
  • ,选最大的
  • ,选最大的
  • ,选最大的两个 (或 ,一样大)。

,输出

复杂度

  • 时间:,对每个 枚举 算点积 ,排序
  • 空间:

代码

import math

def solve():
    n, d = map(int, input().split())
    vecs = []
    for _ in range(n):
        x = list(map(float, input().split()))
        vecs.append(x)
    caps = list(map(int, input().split()))

    normed = []
    for x in vecs:
        rms = math.sqrt(sum(v * v for v in x) / d)
        normed.append([v / rms for v in x])

    S = 0.0
    for j in range(n):
        if caps[j] == 0:
            continue
        scores = []
        for i in range(j):
            dot = sum(normed[i][k] * normed[j][k] for k in range(d))
            scores.append(dot * dot / d)
        scores.sort(reverse=True)
        take = min(caps[j], len(scores))
        S += sum(scores[:take])

    print(round(100 * S))

solve()
#include <bits/stdc++.h>
using namespace std;

int main(){
    int n, d;
    scanf("%d%d", &n, &d);
    vector<vector<double>> v(n, vector<double>(d));
    for(int i = 0; i < n; i++){
        double ss = 0;
        for(int j = 0; j < d; j++){
            scanf("%lf", &v[i][j]);
            ss += v[i][j] * v[i][j];
        }
        double rms = sqrt(ss / d);
        for(int j = 0; j < d; j++) v[i][j] /= rms;
    }
    vector<int> c(n);
    for(int i = 0; i < n; i++) scanf("%d", &c[i]);

    double S = 0;
    for(int j = 1; j < n; j++){
        if(c[j] == 0) continue;
        vector<double> scores;
        for(int i = 0; i < j; i++){
            double dot = 0;
            for(int k = 0; k < d; k++) dot += v[i][k] * v[j][k];
            scores.push_back(dot * dot / d);
        }
        sort(scores.rbegin(), scores.rend());
        int take = min(c[j], (int)scores.size());
        for(int t = 0; t < take; t++) S += scores[t];
    }
    printf("%lld\n", llround(100.0 * S));
    return 0;
}