两个有序数组,从中各取一个组成pair,求和最小的前K个pair(杨氏矩阵top k问题)

 不需要把所有的pair放进优先队列,每次只放当前数的下边和右边的数,其他更远的数,肯定比这两个数更大,

vector<pair<int, int>> topKPair(vector<int> &A, vector<int> &B, int k) {
	vector<pair<int, int>> ans;
	if (A.empty() || B.empty()) return ans;

	priority_queue<pair<int, pair<int, int>>, vector<pair<int, pair<int, int>>>, greater<pair<int, pair<int, int>>>> pq;
	set<pair<int, int>> marked;
	for (pq.push(make_pair(A[0] + B[0], make_pair(0, 0))), marked.insert(make_pair(0, 0)); k > 0 && !pq.empty(); --k) {
		auto p = pq.top(); pq.pop();
		int i = p.second.first, j = p.second.second;
		ans.push_back(make_pair(A[i], B[j]));

		auto down = make_pair(i + 1, j);
		if (i + 1 < A.size() && marked.find(down) == marked.end()) {
			pq.push(make_pair(A[i + 1] + B[j], down));
			marked.insert(down);
		}
		auto right = make_pair(i, j + 1);
		if (j + 1 < B.size() && marked.find(right) == marked.end()) {
			pq.push(make_pair(A[i] + B[j + 1], right));
			marked.insert(right);
		}
	}
	return ans;
}

丑数问题也可以用堆,即,每得到下一个丑数,把这个数和 {2, 3, 5 } 相乘的结果加进堆,只不过这个堆要支持去重,所以实际用set 做

这一类问题的特点:从小到大输出序列,每输出一个值,把和这个值相邻的序列值放入堆。



我们将原本使用 `pair<int, int> a[100005]` 存储任务收益和截止时间的方式,改为使用**两个独立的组 `p[]` 和 `d[]`** 来分别存储。 这在某些场景下更高效(比如只需要访问某一维时),也更容易与旧代码兼容或用于特定优化。 --- ### ✅ 原始代码(使用 pair) ```cpp pair<int, int> a[100005]; sort(a, a + n, [](const pair<int,int>& x, const pair<int,int>& y) { return x.second < y.second; }); ``` 现在我们要改造成: ```cpp int p[100005], d[100005]; // 分开存储收益和截止时间 ``` 但问题来了:**不能直接对两个组排序**。我们需要一种方式让 `p[i]` 和 `d[i]` 能“一起”按 `d[i]` 排序。 --- ## ✅ 解决方案:引入索引组 + 自定义排序 我们创建一个 **索引组 `idx[]`**,然后对这个索引组排序,使得 `d[idx[i]]` 是升序的。 之后通过 `idx` 访问对应的 `p` 和 `d`。 --- ### ✅ 修改后的完整代码 ```cpp #include <bits/stdc++.h> using namespace std; const int MAXN = 100005; int n; int p[MAXN], d[MAXN]; // 分开的两个组 int idx[MAXN]; // 索引组:idx[i] 表示第 i 小的元素原始下标 priority_queue<int> pq; // 用负模拟小根堆 int main() { ios::sync_with_stdio(0), cin.tie(0); cin >> n; for (int i = 0; i < n; ++i) { cin >> p[i] >> d[i]; idx[i] = i; // 初始化索引 } // 对索引组排序:按 d[idx[i]] 升序 sort(idx, idx + n, [](int i, int j) { return d[i] < d[j]; }); // 按排序后的顺序处理任务 for (int k = 0; k < n; ++k) { int i = idx[k]; // 当任务的真实下标 pq.push(-p[i]); if ((int)pq.size() > d[i]) { pq.pop(); } } long long ans = 0; while (!pq.empty()) { ans += -pq.top(); pq.pop(); } cout << ans << '\n'; } ``` --- ### 🔍 关键点解释 | 部分 | 说明 | |------|------| | `idx[i] = i` | 初始每个索引指向自己 | | `sort(idx, idx + n, [](int i, int j){ return d[i] < d[j]; })` | 比较的是 `d` 组中对应位置的值 | | `int i = idx[k]` | 获排序后第 `k` 个任务的原始下标 | | `p[i], d[i]` | 使用原始组访问对应据 | 这样就实现了: - 不用 `pair` - 据仍能按 `d[i]` 排序 - 所有关联性保持正确 --- ### ✅ 优点 | 优势 | 说明 | |------|------| | 内存连续访问 | `p[]` 和 `d[]` 各自连续,利于缓存 | | 易于扩展 | 后续可加权重、状态等字段为独立组 | | 更清晰语义 | `p[i]` 就是收益,`d[i]` 就是截止时间 | --- ### ⚠️ 注意事项 - 排序不再是“原地”的,而是通过索引间接进行 - 所有后续访问都要通过 `idx` 组 - 如果还有其他字段(如任务名、优先级),也要同步用 `idx` 访问 --- ### 💡 替代思路(不推荐):手动同步交换 你也可以不用索引组,而是在排序的同时手动交换 `p[i]` 和 `d[i]` —— 但这需要写复杂的 swap 逻辑,容易出错。 所以 **索引组法是最安全、最通用的方法** --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值