UVa - 11997 - K Smallest Sums

本文介绍了解决UVA 11664问题的方法,通过预处理输入矩阵并对每行进行排序,利用优先队列实现多路归并,最终找出k个数的最小和。适用于竞赛编程及算法学习。

题意:输入一个数k(2 <= k <= 750),然后输入k*k的矩阵,元素为不超过1,000,000的正整数,k行每行取一个数出来相加,求最小的前k个和。

题目链接:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=229&page=show_problem&problem=3148

——>>k个数的和最小,那么任意两行的那两个数的和也最小,否则就可以找到比该值更小的数,所以,可以先求两行中k个最小和,再进行多路归并即可。

#include <iostream>
#include <algorithm>
#include <queue>

using namespace std;

const int maxn = 750 + 10;      //每行最多可能有750个数

struct Item     //定义结点类型
{
    int s;      //s = A[i] + B[j]
    int b;      //b = j即B[j]的下标
    Item(int ss, int bb):s(ss), b(bb){}
    bool operator < (const Item& e) const       //重载运算符,使优先队列中的元素按s的值从小到大地排
    {
        return s > e.s;
    }
};

void merge(int *A, int *B, int k)       //将表A与表B合并,即求当k = 2时的k个最小和
{
    int i;
    priority_queue<Item> pq;
    for(i = 0; i < k; i++)      //第一列元素入列
        pq.push(Item(A[i]+B[0], 0));
    for(i = 0; i < k; i++)      //找出k个最小和
    {
        Item item = pq.top();       //取出队列中最小的
        pq.pop();
        A[i] = item.s;
        int b = item.b;
        if(b+1 < k)     //如果不是最后一个,就加入第“1”列的行列中去
            pq.push(Item(item.s-B[b]+B[b+1], b+1));
    }
}

int a[maxn][maxn];      //输入的k*k数组
int main()
{
    int k, i, j;
    while(cin>>k)
    {
        for(i = 0; i < k; i++)
        {
            for(j = 0; j < k; j++)
                cin>>a[i][j];
            sort(a[i], a[i]+k);     //排个序
        }
        for(i = 1; i < k; i++)
            merge(a[0], a[i], k);       //两两合并
        for(i = 0; i < k-1; i++)        //输出结果
            cout<<a[0][i]<<" ";
        cout<<a[0][k-1]<<endl;
    }
    return 0;
}



合并K个升序链表有多种解决方案,以下为你介绍分治法和优先队列法: ### 分治法 分治法的核心思路是将链表数组不断分割,然后两两合并。mergeLists函数使用分治法将链表数组中的链表合并,mergeTwoLists函数用于合并两个有序链表,返回合并后的有序链表。通过递归调用mergeLists函数,不断分割链表数组,然后合并两部分,最终得到合并后的升序链表。该方法的时间复杂度为O(N log K),其中N是所有链表的节点总数,K是链表的数量 [^3]。 以下是Python实现的示例代码: ```python class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next def mergeTwoLists(l1, l2): dummy = ListNode(0) tail = dummy while l1 and l2: if l1.val < l2.val: tail.next = l1 l1 = l1.next else: tail.next = l2 l2 = l2.next tail = tail.next tail.next = l1 if l1 else l2 return dummy.next def mergeLists(lists, left, right): if left == right: return lists[left] if left > right: return None mid = (left + right) // 2 l1 = mergeLists(lists, left, mid) l2 = mergeLists(lists, mid + 1, right) return mergeTwoLists(l1, l2) def mergeKLists(lists): if not lists: return None return mergeLists(lists, 0, len(lists) - 1) ``` ### 优先队列法 优先队列法利用优先队列来高效地找到最小的节点并合并。该方法的时间复杂度是O(N log k),其中N是所有链表中节点的总数,k是链表的数量。空间复杂度是O(k),因为需要存储所有链表的头节点在堆中 [^4]。 以下是Python实现的示例代码: ```python import heapq from typing import List class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next def __lt__(self, other): return self.val < other.val def mergeKLists(lists: List[ListNode]) -> ListNode: dummy = ListNode(0) tail = dummy heap = [] for head in lists: if head: heapq.heappush(heap, head) while heap: smallest = heapq.heappop(heap) tail.next = smallest tail = tail.next if smallest.next: heapq.heappush(heap, smallest.next) return dummy.next ``` ### 示例 输入:lists = [[1,4,5],[1,3,4],[2,6]] 优先队列法:堆初始包含1、1、2,每次取最小节点,依次拼接,最终得到1→1→2→3→4→4→5→6。分治合并法:递归拆分合并,最终结果一致 [^5]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值