P1392 取数[堆]

题目描述

在一个n行m列的数阵中,你须在每一行取一个数(共n个数),并将它们相加得到一个和。对于给定的数阵,请你输出和前k小的取数方法。

解析

写这题完全自闭。

根本没联想起远古时期做的 P1631 序列合并 ,这题几乎是我刚入门做的了,代码还是仿的。

真的想了很久,怀疑自己智商.jpg。



首先如果做了 P1631 序列合并 而且还记得,那么这道题其实很好做。实际上就是把求两行的前\(k\)小和改成了求\(n\)行的前\(k\)小和。

两行的情况:设单调递增数列\(a,b\),其存在最小值\(a_1+b_1\),次小值为\(min(a_2+b_1,a_1+b_2)\)。

推广到一般,对于第\(k\)小值\(a_n+b_m\),我们有第\(k+1\)小值\(min(a_n+b_{m+1},a_{n+1}+b_m)\)。

因此有算法,我们将\(a_1+b_1\)加入优先队列,不断扩展,每次可扩展出两个可行解,执行\(k\)次,所以显然也有最终答案下标一定不会超过\(k\)。

\(n\)行的情况,我们在行与行间进行迭代,应用与两行情况类似的算法。

换句话说,对于递增的行\(x,y,z\),该算法就是先求\(x,y\)的前\(k\)小和,这些和组成新的数列,再跟\(z\)执行该算法,可以求出\(x,y,z\)三行的前\(k\)小和。

简单证明:

设\(x,y\)的前\(k\)小和构成的数列为\(x'\),\(sum(i,j)\)表示从第\(i,j\)行分别任意取一个元素相加得到的新数列。如果\(z\)中有一元素\(q\),其与一元素\(p\in sum(x,y)\),且\(p \notin x'\)相加构成\(sum(x',z)\)的第\(k'\)小值,且\(k'<k\)。那么一定有一元素\(p' \in x'\)与\(q\)构成更优的第\(k'\)小值,故这样的\(p\)不存在。即\(sum(x',z)=sum(x,y,z)\)。也就是说这个迭代是没有问题的。

参考代码

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#define N 801
using namespace std;
inline int read()
{
    int f=1,x=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}
struct node{
    int val,ap,bp;
    bool operator<(const node &a)const{
        return a.val<val;
    }
    node(){}
    node(int _v,int _a,int _b){val=_v,ap=_a,bp=_b;}
};
priority_queue<node> q;
int a[N],b[N],n,m,k,t[N];
int main()
{
    n=read(),m=read(),k=read();
    for(int i=1;i<=m;++i) a[i]=read();
    sort(a+1,a+m+1);
    for(int i=2;i<=n;++i){
        for(int j=1;j<=m;++j) b[j]=read();
        sort(b+1,b+m+1);
        while(q.size()) q.pop();//换成开个新的优先队列或许更快?
        for(int j=1;j<=k;++j) q.push(node(a[j]+b[1],j,1));//小优化
        for(int j=1;j<=k;++j){
            node x=q.top();q.pop();
            t[j]=x.val;
            if(x.bp<m) q.push(node(a[x.ap]+b[x.bp+1],x.ap,x.bp+1));
        }
        memcpy(a,t,sizeof(a));//迭代
    }
    for(int i=1;i<=k;++i) printf("%d ",a[i]);
    return 0;
}

原文地址:https://www.cnblogs.com/DarkValkyrie/p/11515461.html

时间: 2024-08-30 08:57:58

P1392 取数[堆]的相关文章

P2774 方格取数问题 网络最大流 割

P2774 方格取数问题:https://www.luogu.org/problemnew/show/P2774 题意: 给定一个矩阵,取出不相邻的数字,使得数字的和最大. 思路: 可以把方格分成两个部分,横坐标和纵坐标和为奇数的一组,和为偶数的一组,超级源点向偶数一组连容量为格点数字大小的边,奇数一组向超级汇点连容量为格点大小的边.然后两组间相临的点连容量为无穷的边. 跑出这个图的最大流,相当于是最小割,就是去掉了最少的部分使得网络不流通.因此答案就是sum - dinic(): #inclu

hdu 1565 方格取数(2)(网络流之最大点权独立集)

题目链接:hdu 1565 方格取数(2) 题意: 有一个n*m的方格,每个方格有一个数,现在让你选一些数.使得和最大. 选的数不能有相邻的. 题解: 我们知道对于普通二分图来说,最大独立点集 + 最小点覆盖集 = 总点数,类似的,对于有权的二分图来说,有: 最大点权独立集 + 最小点权覆盖集 = 总点权和, 这个题很明显是要求 最大点权独立集 ,现在 总点权 已知,我们只要求出来 最小点权覆盖集 就好了,我们可以这样建图, 1,对矩阵中的点进行黑白着色(相邻的点颜色不同),从源点向黑色的点连一

HDU 3657 Game(取数 最小割)经典

Game Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1065    Accepted Submission(s): 449 Problem Description onmylove has invented a game on n × m grids. There is one positive integer on each g

HDU 1569 - 方格取数(2) - [最大点权独立集与最小点权覆盖集]

嗯,这是关于最大点权独立集与最小点权覆盖集的姿势,很简单对吧,然后开始看题. HDU1569: Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Problem Description 给你一个m*n的格子的棋盘,每个格子里面有一个非负数.从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取数所在的2个格子不能相邻,并且取出的数的和最大. Input 包括多个测试实例,

NOIp模拟3 取数游戏

试题描述 有一个取数的游戏.初始时,给出一个环,环上的每条边上都有一个非负整数.这些整数中至少有一个0.然后,将一枚硬币放在环上的一个节点上.两个玩家就是以这个放硬币的节点为起点开始这个游戏,两人轮流取数,取数的规则如下: (1)选择硬币左边或者右边的一条边,并且边上的数非0: (2)将这条边上的数减至任意一个非负整数(至少要有所减小): (3)将硬币移至边的另一端. 如果轮到一个玩家走,这时硬币左右两边的边上的数值都是0,那么这个玩家就输了.如下图,描述的是Alice和Bob两人的对弈过程,其

1002 数塔取数问题

1002 数塔取数问题 基准时间限制:1 秒 空间限制:131072 KB 分值: 5 难度:1级算法题 一个高度为N的由正整数组成的三角形,从上走到下,求经过的数字和的最大值. 每次只能走到下一层相邻的数上,例如从第3层的6向下走,只能走到第4层的2或9上. 5 8 4 3 6 9 7 2 9 5 例子中的最优方案是:5 + 8 + 6 + 9 = 28 Input 第1行:N,N为数塔的高度.(2 <= N <= 500) 第2 - N + 1行:每行包括1层数塔的数字,第2行1个数,第3

P1004 方格取数

P1004 方格取数 题目描述 设有N*N的方格图(N<=9),我们将其中的某些方格中填入正整数,而其他的方格中则放 人数字0.如下图所示(见样例): A 0 0 0 0 0 0 0 0 0 0 13 0 0 6 0 0 0 0 0 0 7 0 0 0 0 0 0 14 0 0 0 0 0 21 0 0 0 4 0 0 0 0 15 0 0 0 0 0 0 14 0 0 0 0 0 0 0 0 0 0 0 0 0 0 . B 某人从图的左上角的A点出发,可以向下行走,也可以向右走,直到到达右下角

51nod 1084 矩阵取数问题 V2

1084 矩阵取数问题 V2 基准时间限制:2 秒 空间限制:131072 KB 一个M*N矩阵中有不同的正整数,经过这个格子,就能获得相应价值的奖励,先从左上走到右下,再从右下走到左上.第1遍时只能向下和向右走,第2遍时只能向上和向左走.两次如果经过同一个格子,则该格子的奖励只计算一次,求能够获得的最大价值. 例如:3 * 3的方格. 1 3 3 2 1 3 2 2 1 能够获得的最大价值为:17.1 -> 3 -> 3 -> 3 -> 1 -> 2 -> 2 -&g

ROS取数线程分析(4): 不带组装: socket选项SO_SNDBUF,SO_RCVBUF对带宽和CPU的影响(2)

在不带组装,取数线程简化为直接while循环recv,通过setsockopt将SO_SNDBUF, SO_RCVBUF设置为256*1024时,短时间内的测试结果为6.7Gb/s.但是长时间的测试结果却如下图: 上图的横坐标为ROS接收到event的个数,以40000为单位. (x, y)表示 ROS接收到第 x*40000 个 event 时,接收端的带宽为 y Gbits/s. event size 为 2KB. 由上图可以看出,接收端的带宽在保持了一段时间的6Gb/s后,是不断降低的.但