UVA 11997 - K Smallest Sums(多路归并)

题目链接:点击打开链接

思路:多路归并。 用贪心的规则, 我们先考虑两个数组的情况, 如果将两个数组从小到大排序, 那么我们先将数组a的所有元素加上数组b的最小的元素, 然后放入优先队列。 因为是事先排好序的, 所以其中肯定有一个是最小的,那么当这个数取出来之后, 如何保证队列头上时次小的呢? 我们发现, a数组的所有元素都用上了, 而且是和b数组中最小的数相加得到的, 所以, 当a[0]+b[0]被取出, 下一个可能有用的和一定不是a[1]+b[0], 因为已经在队列中了, 那么只能是a[0] + b[1]。 贪心的每次取出一个数之后再加进去一个尽量小的值。

本题进行k次就行了。

细节参见代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
#include <stack>
#include <bitset>
#include <cstdlib>
#include <cmath>
#include <set>
#include <list>
#include <deque>
#include <map>
#include <queue>
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
typedef long double ld;
const ld eps = 1e-9, PI = 3.1415926535897932384626433832795;
const int mod = 1000000000 + 7;
const int INF = 0x3f3f3f3f;
// & 0x7FFFFFFF
const int seed = 131;
const ll INF64 = ll(1e18);
const int maxn = 1000;
int T,n,m,a[maxn][maxn];
struct node {
    int s, b;
    node(int s=0, int b=0):s(s), b(b) {}
    bool operator < (const node& rhs) const {
        return s > rhs.s;
    }
};
void bin(int* a, int *b, int *c, int n) {
    priority_queue<node> q;
    for(int i = 0; i < n; i++) q.push(node(a[i]+b[0], 0));
    for(int i = 0; i < n; i++) {
        node cur = q.top(); q.pop();
        c[i] = cur.s;
        int id = cur.b;
        if(id+1 < n) q.push(node(cur.s-b[id]+b[id+1], id+1));
    }
}
int main() {
    while(~scanf("%d",&n)) {
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < n; j++) scanf("%d",&a[i][j]);
            sort(a[i], a[i]+n);
        }
        for(int i = 1; i < n; i++) {
            bin(a[0], a[i], a[0], n);
        }
        for(int i = 0; i < n; i++) {
            printf("%d%c", a[0][i], i == n-1 ? '\n' : ' ');
        }
    }
    return 0;
}



### 关于四路归并排序的图解说明 #### 四路归并排序概述 四路归并排序是一种扩展形式的多路归并排序,其基本思想与传统的2-归并排序相似。不同之处在于每次合并时不是将两个有序子序列合并为一个,而是将四个有序子序列合并为一个更大的有序序列[^5]。 #### 工作流程描述 假设初始序列为长度 $ N $ 的无序数组,则可以将其视为由 $ N $ 个长度为1的有序子序列组成。接着按照如下方式逐步完成排序: 1. 将每相邻的4个长度为1的子序列合并成一个长度为4(或更少)的有序子序列; 2. 继续对这些新的较长的有序子序列再次按上述方式进行分组和合并操作; 3. 不断重复此过程直到最终形成一个完整的长度为 $ N $ 的有序序列。 以下是具体的图示化表示: #### 图解实例展示 ##### 初始状态 假设有以下待排序列表 `[7, 2, 9, 1, 0, 3, 8, 4]` ,我们将它分成多个单元素的小数组: ``` [[7], [2], [9], [1], [0], [3], [8], [4]] ``` ##### 第一次合并 (每4个小数组合并) 现在把这八个单独的数值两两配对,并进一步组合成为四个较小范围内的已排序片段: ```python # 合并前 [[7, 2], [9, 1], [0, 3], [8, 4]] # 对每个小组内部进行局部排序后得到的结果 [[2, 7], [1, 9], [0, 3], [4, 8]] ``` ##### 第二次合并 (继续扩大规模至整个数据集) 下一步就是把这些已经部分整理好的区块再度联合起来构成更大区域上的顺序排列情况: ```python # 再次合并之前的状况 [[2, 7, 1, 9], [0, 3, 4, 8]] # 这些较大单元间执行全局排序后的成果展现出来 [[1, 2, 7, 9], [0, 3, 4, 8]] ``` 最后一步便是将这两个大块合为一体即可获得完全排好队列的形式: ```python # 总体上最后一次融合动作完成后呈现出来的样子 [0, 1, 2, 3, 4, 7, 8, 9] ``` 以上便是一个简单的四路归并排序的过程演示。 #### 实现代码样例 下面给出一段 Python 版本实现该算法逻辑的例子供参考学习使用: ```python def merge_four_way(arr): if len(arr) <= 1: return arr mid1 = len(arr)//4 mid2 = mid1*2 mid3 = mid1*3 left = merge_four_way(arr[:mid1]) second = merge_four_way(arr[mid1:mid2]) third = merge_four_way(arr[mid2:mid3]) right = merge_four_way(arr[mid3:]) return four_way_merge(left, second, third, right) def four_way_merge(l1,l2,l3,l4): result = [] while l1 or l2 or l3 or l4: smallest = None for lst in [l1, l2, l3, l4]: if lst and (smallest is None or lst[0]<smallest[0]): smallest = lst if smallest: result.append(smallest.pop(0)) return result ``` 通过这样的函数定义我们可以轻松调用 `merge_four_way()` 方法传入任意未加工的数据集合从而获取到经过精确计算处理之后返回来的理想型态下的输出结果。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值