UVA ~ 11997 ~ K Smallest Sums (优先队列 + 多路归并)

题意:有k个整数数组,各包含k个元素。在每个数组中取一个元素加起来,可以得到k^k个和。求这些和中最小的k个值(重复的值算多次)。

【输入格式】

输入包含多组数据。每组数据第一行为一个整数k(1<=k<=750)。以下k行每行包含k个不超过10^6的正整数。输入结束标志为EOF。输入文件不超过5MB。

【输出格式】

对于每组数据,输出k个最小和的值,并按照从小到大排序。

【分析】

在解决这个问题之前,先看他的简化版:给出两个长度为n的有序表A和B,分别在A和B中任取一个数并相加,可以得到n^2个和。求这些和中最小的n个。

这个问题可以转化为前面介绍过的多路归并问题。这需要我们把这n^2个和组织成如下n个有序表。

表1:A1+B1≤A1+B2≤A1+B3≤...

表2:A2+B1≤A2+B2≤A2+B3≤...

表3:An+B1≤An+B2≤An+B3≤...

其中第a张表里的元素形如Aa+Bb。我们用二元组(s,b)来表示一个元素,其中s=Aa+Bb。为什么不保存A的下标a呢?因为我们用不到a的值。如果我们需要得到一个元素(s,b)在表a中的下一个元素(s',b+1),只需要计算s'=Aa+B(b+1)=Aa+Bb-Bb+B(b+1)=s-Bb+B(b+1),并不需要知道a是多少。代码里可以用如下结构体来表示。

struct Item
{
    int s, b;//s=A[a]+B[b]
    Item (int _s, int _b): s(_s), b(_b) {}
    bool operator < (const Item& that) const
    {
        return that.s < s;
    }
};

以上内容来自算法竞赛入门经典

AC代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 755;
struct Item
{
    int s, b;
    Item (int _s, int _b): s(_s), b(_b) {}
    bool operator < (const Item& that) const
    {
        return that.s < s;
    }
};
void Merge(int* A, int* B, int* C, int n)
{
    priority_queue<Item> PQ;
    for (int i = 0; i < n; i++)
        PQ.push(Item(A[i]+B[0], 0));
    for (int i = 0; i < n; i++)
    {
        Item t = PQ.top(); PQ.pop();
        C[i] = t.s;
        int b = t.b;
        if (b+1 < n) PQ.push(Item(t.s-B[b]+B[b+1], b+1));
    }
}
int n, A[MAXN][MAXN];
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++)
            Merge(A[0], A[i], A[0], n);
        for (int i = 0; i < n; i++)
        {
            printf("%d", A[0][i]);
            if (i != n-1) printf(" ");
            else printf("\n");
        }
    }
    return 0;
}
/*
3
1 8 5
9 2 5
10 7 6
2
1 1
1 2
*/


空间优化:

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 755;
struct Item
{
    int s, b;//s=A[a]+B[b]
    Item (int _s, int _b): s(_s), b(_b) {}
    bool operator < (const Item& that) const
    {
        return that.s < s;
    }
};
void Merge(int* A, int* B, int* C, int n)
{
    priority_queue<Item> PQ;
    for (int i = 0; i < n; i++)
        PQ.push(Item(A[i]+B[0], 0));
    for (int i = 0; i < n; i++)
    {
        Item t = PQ.top(); PQ.pop();
        C[i] = t.s;
        int b = t.b;
        if (b+1 < n) PQ.push(Item(t.s-B[b]+B[b+1], b+1));
    }
}
int n, A[2][MAXN];
int main()
{
    while (~scanf("%d", &n))
    {
        for (int i = 0; i < n; i++) scanf("%d", &A[0][i]);
        for (int i = 1; i < n; i++)
        {
            for (int j = 0; j < n; j++)
            {
                scanf("%d", &A[1][j]);
            }
            sort(A[1], A[1] + n);
            Merge(A[0], A[1], A[0], n);
        }
        for (int i = 0; i < n; i++)
        {
            printf("%d", A[0][i]);
            if (i != n-1) printf(" ");
            else printf("\n");
        }
    }
    return 0;
}
/*
3
1 8 5
9 2 5
10 7 6
2
1 1
1 2
*/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Prim算法是一种最小生成树算法,可以通过使用优先队列来提高算法效率。以下是使用C语言实现Prim算法的示例代码: ```c #include <stdio.h> #include <limits.h> #define V 5 // 顶点数 #define INF INT_MAX // 定义无穷大 int graph[V][V] = {{0, 2, 0, 6, 0}, {2, 0, 3, 8, 5}, {0, 3, 0, 0, 7}, {6, 8, 0, 0, 9}, {0, 5, 7, 9, 0}}; // 图的邻接矩阵表示 int primMST() { int key[V]; // 存储每个顶点到MST的最小距离 int parent[V]; // 存储每个顶点的父节点 int visited[V]; // 存储每个顶点是否已经加入MST for (int i = 0; i < V; i++) { key[i] = INF; visited[i] = 0; } key[0] = 0; parent[0] = -1; // 创建优先队列 struct node { int vertex; int key; }; struct priority_queue { int size; struct node heap[V]; } pq; pq.size = 0; // 将第一个顶点加入优先队列 struct node n; n.vertex = 0; n.key = key[0]; pq.heap[pq.size++] = n; while (pq.size > 0) { // 从优先队列中取出距离MST最近的顶点 int u = pq.heap[0].vertex; pq.heap[0] = pq.heap[--pq.size]; int i = 0; while (2 * i + 1 < pq.size) { int left = 2 * i + 1; int right = 2 * i + 2; int smallest = i; if (pq.heap[left].key < pq.heap[smallest].key) { smallest = left; } if (right < pq.size && pq.heap[right].key < pq.heap[smallest].key) { smallest = right; } if (smallest == i) { break; } struct node temp = pq.heap[i]; pq.heap[i] = pq.heap[smallest]; pq.heap[smallest] = temp; i = smallest; } // 将该顶点加入MST visited[u] = 1; // 更新与该顶点相邻的顶点到MST的最小距离 for (int v = 0; v < V; v++) { if (graph[u][v] && !visited[v] && graph[u][v] < key[v]) { key[v] = graph[u][v]; parent[v] = u; // 将该顶点加入优先队列 struct node n; n.vertex = v; n.key = key[v]; pq.heap[pq.size++] = n; int j = pq.size - 1; while (j > 0 && pq.heap[(j - 1) / 2].key > pq.heap[j].key) { struct node temp = pq.heap[(j - 1) / 2]; pq.heap[(j - 1) / 2] = pq.heap[j]; pq.heap[j] = temp; j = (j - 1) / 2; } } } } // 输出MST int weight = 0; for (int i = 1; i < V; i++) { printf("%d - %d : %d\n", parent[i], i, graph[i][parent[i]]); weight += graph[i][parent[i]]; } printf("MST的总权重为:%d\n", weight); } int main() { primMST(); return 0; } ``` 在这个示例代码中,我们首先定义了一个邻接矩阵表示的图,然后实现了Prim算法。在实现Prim算法时,我们使用了一个优先队列来存储距离MST最近的顶点。在每一次迭代中,我们从优先队列中取出距离MST最近的顶点,并将该顶点加入MST。然后,我们更新与该顶点相邻的顶点到MST的最小距离,并将这些顶点加入优先队列。最后,我们输出MST的边和总权重。 需要注意的是,在这个示例代码中,我们使用了一个结构体来表示优先队列中的每一个元素,同时使用了一个结构体来表示优先队列本身。我们还实现了一个删除堆顶元素的函数和一个向堆中插入元素的函数,以保证优先队列的正确性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值