ACWING146. 序列(优先队列)

给定m个序列,每个包含n个非负整数。

现在我们可以从每个序列中选择一个数字以形成具有m个整数的序列。

很明显,我们一共可以得到nm个这种序列, 然后我们可以计算每个序列中的数字之和,并得到nm个值。

现在请你求出这些序列和之中最小的n个值。

输入格式
第一行输入一个整数T,代表输入中包含测试用例的数量。

接下来输入T组测试用例。

对于每组测试用例,第一行输入两个整数m和n。

接下在m行输入m个整数序列,数列中的整数均不超过10000。

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

每组输出占一行。

数据范围
0<m≤1000,
0<n≤2000
输入样例:
1
2 3
1 2 3
2 2 3
输出样例:
3 3 4

思路:
可以确定的点是前n个最小数列的前2个数列的对应数字和,在前两个数列取出两个数的前n个和之中。
否则,一定有一个办法,找到一个更小的替换办法替换掉前两个数列的对应数。
那么可以先求出前2个数的前n小,再由这n个数和第3个数匹配,以此类推。

那么考虑,对于两个数列的情况,如何求2个数和的前n小。这个过程可以列成n*n的数表,并且保证数列是递增顺序的,这样可以以动态规划状态来看待,定义c[k]为第k小的数, f [ x ] [ y ] = a [ x ] + b [ y ] f[x][y] = a[x] + b[y]
那么转移方程为c[k] = min{已经计算出的f[x][y]}
优先队列则是用来优化这个转移过程,维护已经计算出的 f [ x ] [ y ] f[x][y]

当第k小为 f [ x ] [ y ] f[x][y] 的时候,放进优先队列的数位 f [ x + 1 ] [ y ] f[x+1][y]
f [ x ] [ y + 1 ] f[x][y+1]
换言之 f [ x ] [ y ] f[x][y] 可以从 f [ x 1 ] [ y ] f[x-1][y] f [ x ] [ y 1 ] f[x][y-1] 转移过来。
但这样会出现重复。
我们可以设置成 f [ x ] [ y ] f[x][y] 只能从 f [ x ] [ y 1 ] f[x][y-1] 转移过来,这样的话当需要取出 f [ x ] [ y ] f[x][y] 的时候, f [ x ] [ y 1 ] f[x][y-1] 因为比 f [ x ] [ y ] f[x][y] 小,一定已经被计算过了。这个过程只有y = 1的时候才需要计算 f [ x + 1 ] [ 1 ] f[x + 1][1] ,因为在需要 f [ x ] [ y ] f[x][y] 的时候 f [ x ] [ y 1 ] f[x][y-1] 必须已经计算出来了,而y = 1的时候就只能从 f [ x 1 ] [ y ] f[x-1][y] 转移了。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <vector>

using namespace std;

const int maxn = 2005;
int a[maxn],b[maxn],c[maxn];
int m,n;

struct Node
{
    int val,x,y;
    bool operator < (const Node &rhs)const
    {
        return val > rhs.val;
    }
};

priority_queue<Node>Q;

void solve(int i)
{
    for(int i = 1;i <= n;i++)scanf("%d",&b[i]);
    while(!Q.empty())Q.pop();
    sort(b + 1,b + 1 + n);
    Q.push(Node{a[1] + b[1],1,1});
    for(int i = 1;i <= n;i++)
    {
        Node tmp = Q.top();Q.pop();
        c[i] = tmp.val;int x = tmp.x,y = tmp.y;
        if(y == 1)Q.push(Node{a[x + 1] + b[y],x + 1,y});
        Q.push(Node{a[x] + b[y + 1],x,y + 1});
    }
}

int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&m,&n);
        for(int i = 1;i <= n;i++)
        {
            scanf("%d",&a[i]);
        }
        sort(a + 1,a + 1 + n);memcpy(c,a,sizeof(a));
        for(int i = 2;i <= m;i++)
        {
            solve(i);
            memcpy(a,c,sizeof(c));
        }
        for(int i = 1;i <= n;i++)
        {
            printf("%d ",c[i]);
        }
        printf("\n");
    }
    return 0;
}

发布了756 篇原创文章 · 获赞 27 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/tomjobs/article/details/104532308