首页 > 试题广场 >

小红的示例检索重排

[编程题]小红的示例检索重排
  • 热度指数:646 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
小红正在调试一个智能学习助手。这个系统会先从知识库里召回一批候选示例,再从中挑出若干条最适合展示给用户的结果。
如果系统只按“和当前问题有多相关”来排序,往往会返回许多内容非常接近的示例。为了让结果既相关又有区分度,小红决定使用最大边际相关性(MMR)策略做二次重排。
现在一共有 N 个候选文档。第 i 个文档有一个互不相同的文档编号 ID,还给出了它和当前查询的相关性分数 rel[i]。此外,系统还知道任意两个候选文档之间的相似度 sim[i][j]。
小红会维护一个已经选中的文档集合 S,初始时它为空。随后重复执行 K 轮,每一轮都要在还没被选过的文档里,计算当前的 MMR 分数:
MMR(i) = λ * rel[i] - (1 - λ) * max(sim[i][j]),其中 j 来自集合 S。
如果当前 S 为空,那么上式中的最大相似度部分按 0 处理。
每一轮都选出当前 MMR 分数最高的那个文档加入集合 S,并把它的文档编号按顺序记入答案中。如果有多个文档在这一轮的 MMR 分数完全相同,则选择文档编号较小的那个。
请你输出这 K 轮依次选中的文档编号。

输入描述:
第一行一个整数 N,表示候选文档数量,满足 1 <= N <= 1000。
接下来 N 行,每行包含一个浮点数 rel 和一个整数 ID,表示按输入顺序编号的第 i 个候选文档的相关性分数和文档编号。所有文档编号互不相同,且在 1 到 10^9 之间。相关性分数在 [0,1] 范围内。
接下来 N 行,每行包含 N 个浮点数,构成相似度矩阵 sim。其中第 i 行第 j 列表示文档 i 和文档 j 的相似度。题目保证矩阵对称,且 sim[i][i] = 1.00。所有相似度都在 [0,1] 范围内,并精确到小数点后两位。
最后一行包含一个浮点数 λ 和一个整数 K,其中 0 <= λ <= 1,0 <= K <= N。


输出描述:
输出一行,包含 K 个整数,表示按选择顺序得到的文档编号,编号之间用空格分隔。
如果 K = 0,输出一个空行即可。
示例1

输入

4
0.80 11
0.75 7
0.60 30
0.50 25
1.00 0.90 0.20 0.10
0.90 1.00 0.30 0.20
0.20 0.30 1.00 0.80
0.10 0.20 0.80 1.00
0.6 3

输出

11 30 7

说明

第一轮集合为空,只看 0.6 * rel,文档 11 最高。第二轮开始要扣掉与已选文档的最大相似度,文档 30 与 11 的相似度更低,因此先被选出。第三轮继续比较后,得到顺序 11 30 7。
#include <iostream>
#include <vector>
#include <cmath>
#include <iomanip>

using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int N;
    cin >> N;
    vector<double> rel(N);
    vector<int> ids(N);
    for (int i = 0; i < N; ++i) {
        cin >> rel[i] >> ids[i];
    }

    vector<vector<double>> sim(N, vector<double>(N));
    for (int i = 0; i < N; ++i) {
        for (int j = 0; j < N; ++j) {
            cin >> sim[i][j];
        }
    }

    double lambda;
    int K;
    cin >> lambda >> K;

    if (K == 0) {
        cout << endl;
        return 0;
    }

    vector<bool> selected(N, false);
    vector<double> best_sim(N, 0.0);   // 每个文档与当前已选集合的最大相似度
    vector<int> ans;

    const double eps = 1e-9;

    for (int t = 0; t < K; ++t) {
        double best_mmr = -1e9;
        int best_idx = -1;

        for (int i = 0; i < N; ++i) {
            if (selected[i]) continue;
            double mmr = lambda * rel[i] - (1.0 - lambda) * best_sim[i];
            if (best_idx == -1 || mmr > best_mmr + eps) {
                best_mmr = mmr;
                best_idx = i;
            } else if (fabs(mmr - best_mmr) < eps && ids[i] < ids[best_idx]) {
                best_idx = i;
            }
        }

        // 理论上必能找到
        selected[best_idx] = true;
        ans.push_back(ids[best_idx]);

        // 更新 best_sim
        for (int j = 0; j < N; ++j) {
            if (!selected[j]) {
                best_sim[j] = max(best_sim[j], sim[j][best_idx]);
            }
        }
    }

    for (size_t i = 0; i < ans.size(); ++i) {
        if (i > 0) cout << ' ';
        cout << ans[i];
    }
    cout << endl;

    return 0;
}


发表于 2026-05-17 20:50:53 回复(0)