【ACWing】146. 序列

文章描述了一个编程问题,涉及到给定多个序列,求解所有可能的序列和中的最小n个值。当序列数量m=2时,可以使用最小堆来解决,对于m>2的情况,可以两两组合来逐步求解前n大的和。提供的代码示例展示了如何处理这个问题,其时间复杂度为O(mn*logn),空间复杂度为O(mn)。
摘要由CSDN通过智能技术生成

题目地址:

https://www.acwing.com/problem/content/description/148/

给定 m m m个序列,每个包含 n n n个非负整数。现在我们可以从每个序列中选择一个数字以形成具有 m m m个整数的序列。很明显,我们一共可以得到 n m n^m nm个这种序列,然后我们可以计算每个序列中的数字之和,并得到 n m n^m nm个值。现在请你求出这些序列和之中最小的 n n n个值。

输入格式:
第一行输入一个整数 T T T,代表输入中包含测试用例的数量。
接下来输入 T T T组测试用例。
对于每组测试用例,第一行输入两个整数 m m m n n n
接下在 m m m行输入 m m m个整数序列,数列中的整数均不超过 10000 10000 10000

输出格式:
对于每组测试用例,均以递增顺序输出最小的 n n n个序列和,数值之间用空格隔开。每组输出占一行。

数据范围:
0 < m ≤ 1000 0<m≤1000 0<m1000
0 < n ≤ 2000 0<n≤2000 0<n2000

如果 m = 2 m=2 m=2,是容易的,可以用最小堆来做,堆里存三元组 ( y 1 , y 2 , s ) (y_1,y_2,s) (y1,y2,s) y 1 , y 2 y_1,y_2 y1,y2是两个序列分别取到的列下标, s s s是对应的两个数的和。堆顶是 s s s最小的元组。先对两个序列从小到大排序,接着让 ( 1 , 1 , s 0 ) (1,1,s_0) (1,1,s0)入堆,很显然这个 s 0 s_0 s0(即两个序列最小值之和)肯定是最小的。接着分别考虑 ( 2 , 1 , . ) , ( 1 , 2 , . ) (2,1,.),(1,2,.) (2,1,.),(1,2,.),以此类推。注意相同元素不要重复考虑。

如果 m > 2 m>2 m>2,则可以两个两个考虑,每次只求前 n n n大的和即可。代码如下:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;

const int N = 2010, M = 1010;
int m, n;
int a[M][N], c[N];
bool vis[N][N];
struct Node {
  int y1, y2, sum;
};
auto comp = [](Node& p1, Node& p2) { return p1.sum > p2.sum; };

int main() {
  int T;
  scanf("%d", &T);
  while (T--) {
    scanf("%d%d", &m, &n);
    int sum = 0;
    for (int i = 1; i <= m; i++) {
      for (int j = 1; j <= n; j++) scanf("%d", &a[i][j]);
      sort(a[i] + 1, a[i] + 1 + n);
    }
    for (int i = 2; i <= m; i++) {
      priority_queue<Node, vector<Node>, decltype(comp)> heap(comp);
      memset(vis, 0, sizeof(vis));
      heap.push({1, 1, a[1][1] + a[i][1]});
      for (int j = 1; j <= n; j++) {
        auto t = heap.top(); heap.pop();
        c[j] = t.sum;
        int y1 = t.y1, y2 = t.y2;
        // vis用来防止重复考虑
        if (y1 + 1 <= n && !vis[y1 + 1][y2]) {
          heap.push({y1 + 1, y2, a[1][y1 + 1] + a[i][y2]});
          vis[y1 + 1][y2] = true;
        }
        if (y2 + 1 <= n && !vis[y1][y2 + 1]) {
          heap.push({y1, y2 + 1, a[1][y1] + a[i][y2 + 1]});
          vis[y1][y2 + 1] = true;
        }
      }

      for (int j = 1; j <= n; j++) a[1][j] = c[j];
    }

    for (int i = 1; i <= n; i++) printf("%d ", a[1][i]);
    puts("");
  }
}

时间复杂度 O ( m n log ⁡ n ) O(mn\log n) O(mnlogn),空间 O ( m n ) O(mn) O(mn)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值