Source : UVA - 11997 K Smallest Sums
http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=18702
题意
有k个整数数组,各包含k个元素,从每个数组中选取一个元素加起来,可以得到k^k个和,求这些和中最小的k个值。
示例
Sample Input
3
1 8 5
9 2 5
10 7 6
2
1 1
1 2
Sample Output
9 10 12
2 2
思路
二路归并构建二维平面表:
表1:A1+B1<=A1+B2<=A1+B3……表2:A2+B1<=A2+B2<=A2+B3…………表n:An+B1<=An+B2<=An+B3……
首先将第一列入队,第一列的最小值即为全局的最小值;接下来定位到最小值的那一行,跳过这一个元素,把下一个元素入队,始终保持priority_queue中有n个元素,队首元素即为全局下一小的元素和,记录即可。
多路归并就要构建三位立体表:想象下
k×k×k的立方体表格
逐层二路合并即可。
★ 参考刘汝佳《算法竞赛入门经典训练指南》page 190
参考代码
#include<bits/stdc++.h>
using namespace std;
const int _max = 750 + 10;
int n,A[_max],B[_max];
struct Item{
int s,b;//(s,b) s = Aa + Bb
Item (){}
Item(int s,int b):s(s) , b(b){}//构造函数
bool operator < (const Item& a) const{//定义优先级
return s > a.s;
}
};
priority_queue<Item>pq;
Item item;
void merge(int A[],int B[],int C[]){//二路归并,前n个最小和存于C[]
while(!pq.empty()) pq.pop();
for(int i = 0; i < n; ++ i){
item = Item(A[i] + B[0],0);
pq.push(item); //第一列元素入队
}
for(int i = 0; i < n; ++ i){//O(nlogn)
item = pq.top();pq.pop();
C[i] = item.s;//第一列最小的一定是全局最小,跳过它读取同一行的下一个元素入队,循环n次
int b = item.b;
if(b + 1 < n) pq.push(Item(item.s - B[b] + B[b+1],b + 1));
}
}
int main(){
#ifndef ONLINE_JUDGE
freopen("input.txt","r",stdin);
#endif // ONLINE_JUDGE
while(scanf("%d",&n) == 1){
for(int i = 0; i < n; ++ i) //先读一路数据
scanf("%d",A + i);
sort(A , A + n);
for(int i = 1; i < n; ++ i){ //以二路为基础做多路归并
for(int j = 0; j < n; ++ j) scanf("%d",B+j);
sort(B,B+n);
merge(A,B,A);//A[]元素只用做初始化,所以在合并过程可以直接把结果存储到A中
}
printf("%d",A[0]);//输出格式
for(int i = 1; i < n; ++ i) printf(" %d",A[i]);
printf("\n");
}
return 0;
}
- 加粗
Ctrl + B
- 斜体
Ctrl + I
- 引用
Ctrl + Q
- 插入链接
Ctrl + L
- 插入代码
Ctrl + K
- 插入图片
Ctrl + G
- 提升标题
Ctrl + H
- 有序列表
Ctrl + O
- 无序列表
Ctrl + U
- 横线
Ctrl + R
- 撤销
Ctrl + Z
- 重做
Ctrl + Y
版权声明:本文为博主原创文章,未经博主允许不得转载。
时间: 2024-11-08 11:05:05