首页 > 试题广场 >

基于空间连续块的稀疏注意力机制

[编程题]基于空间连续块的稀疏注意力机制
  • 热度指数:248 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解

为提升长序列推理效率,现定义一套“分块压缩 + 打分 + 二段划分”的流程。给定长度为 n、维度为 d 的向量序列 X0..X(n-1)(均非零)与块大小 b,将序列按顺序切成相邻的 m=ceil(n/b) 个连续块,每块至多 b 个向量(最后一块可不足 b 个)。

对第 k 个块(1≤k≤m):

  • 先做按维度的“块内均值”得到向量 h_k(每一维为该块该维的平均值)。
  • 设常数 b1=2、b2=1,并给定长度为 d 的向量 W1、W2。定义
    s_k = W1 · h_k + b1,z_k = max(0, s_k),c_k = W2 * z_k + b2
    其中 “W2 * z_k” 表示用标量 z_k 逐分量缩放 W2,c_k 与 W2 同为 d 维。
  • 定义 q 为全 1 的 d 维向量,得到标量打分
    a_k = (q · c_k) / sqrt(d) = (c_k 各分量之和) / sqrt(d)。

得到序列 A = (a_1, a_2, ..., a_m) 后,把 A 恰好切成 2 段且两段都非空、彼此连续,记两段元素和分别为 S1、S2,目标最大化 min(S1, S2)。记最优值为 S,输出 round(100*S) 的整数(四舍五入到最近整数)。



输入描述:
  • 第一行:n d b
  • 接下来 n 行:每行 d 个实数,依次为 X0..X(n-1)
  • 倒数第 2 行:d 个实数,表示 W1
  • 最后一行:d 个实数,表示 W2


输出描述:
一行一个整数:round(100*S)
示例1

输入

4 1 2
1
3
2
5
1.5
2

输出

1100

说明

  • 分块:B1=[1,3],B2=[2,5]。h1=2.0,h2=3.5。
  • b1=2,b2=1,W1=1.5,W2=2。
    • s1=1.52+2=5 → z1=5 → c1=25+1=11 → a1=11/√1=11
    • s2=1.53.5+2=7.25 → z2=7.25 → c2=27.25+1=15.5 → a2=15.5
  • A=[11,15.5],唯一切分 [11]|[15.5],min=11,输出 round(1100)=1100。


备注:
本题由牛友@Charles 整理上传
牛客上数据变小了还是真题数据本来就这么小啊,纯暴力递归都能A
#include "bits/stdc++.h"
using namespace std;

int n,m,d,b;
const double INF = 1e18;
vector<double> w1,w2;
vector<vector<double>> mat;

vector<double> handle(vector<vector<double>> block, int start, int end) {
    int col = block[0].size();
    vector<double> res(col);
    double sum;
    for(int i = 0; i < col; i++){
        sum = 0.0;
        for(int j = start; j < end; j++){
            sum = sum + block[j][i];
        }
        res[i] = sum / (end - start);
    }
    return res;
}

double dot(vector<double> vector1, vector<double> vector2) {
    double res = 0.0;
    for(int i = 0; i < vector1.size(); i++){
        res = res + vector1[i] * vector2[i];
    }
    return res;
}

vector<double> calc(vector<double> vector1, double d, int b2) {
    int sz = vector1.size();
    vector<double> res(sz);
    for(int i = 0; i < sz; i++){
        res[i] = vector1[i] * d + b2;
    }
    return res;
}

double dfs(int idx, double s1, double s2, bool f1, bool f2, vector<double> ak,int m) {
    // 已经分配结束
    if (idx == m){
        // 保证两段都有值
        if(f1 && f2){
            return fmin(s1,s2);
        }else{
            return -INF; // 返回一个极大值
        }
    }
    double op1,op2;
    // 将当前数分给s1
    op1 = dfs(idx+1,s1 + ak[idx],s2, true,f2,ak,m);
    // 分给s2
    op2 = dfs(idx+1,s1,s2 + ak[idx],f1, true,ak,m);
    return fmax(op1,op2);
}

int main(){
    // 输入
    cin >> n >> d >> b;
    mat.resize(n,vector<double>(d,0.0));
    for(int i = 0; i < n; i++){
        for(int j = 0; j < d; j++){
            cin >> mat[i][j];
        }
    }
    w1.resize(d,0.0);
    w2.resize(d,0.0);
    for(int i = 0; i < d; i++){
        cin >> w1[i];
    }
    for(int i = 0; i < d; i++){
        cin >> w2[i];
    }
    // 模拟
    double S = -INF;
    // m为块数
    m = n / b;
    if (n % b != 0) m++;
    vector<vector<double>> hk(m,vector<double>(n,0.0));
    vector<double> sk(m), zk(m), ak(m);
    vector<vector<double>> ck(m,vector<double>(n,0.0));
    vector<double> q(d,1.0);
    int b1 = 2, b2 = 1;
    // 求h1...hm
    for(int i = 1; i <= m; i++) {
        hk[i-1] = handle(mat, (i - 1) * b, min(i * b, n));
        sk[i-1] = dot(w1,hk[i-1]) + b1;
        zk[i-1] = fmax(0.0,sk[i-1]);
        ck[i-1] = calc(w2,zk[i-1],b2);
        ak[i-1] = dot(q,ck[i-1]) / sqrt(d);
    }
    // 将ak分为两段,和分别为S1,S2,最大化min(S1,S2)
    if (m == 1) S = ak[0];
    else S = fmax(S,dfs(0,0,0,false,false,ak,m));
    // 输出
    cout << round(100 * S);
    return 0;
}


发表于 2026-01-12 17:04:15 回复(0)