[UVA 11997] K Smallest Sums

本文介绍了一种解决多个有序序列寻找前n个最小值和的高效算法。通过使用优先队列维护二元组,每次迭代更新最小值,并通过代码实现展示了如何处理多个序列的数据结构操作。

图片加载可能有点慢,请跳过题面先看题解,谢谢
1196604-20171017201901396-1598246053.png
1196604-20171017201905834-1002574892.png

我们先考虑两个序列的情况(以下这一段蒯自 刘汝佳训练指南):
这个问题可以转换成一个多路归并问题,有这样一个含有 \(n^2\) 个元素的表:

  1. \(A[1]+B[1]\leq A[1]+B[2]\leq A[1]+B[3]\leq\) ...
  2. \(A[2]+B[1]\leq A[2]+B[2]\leq A[2]+B[3]\leq\) ...
  3. \(A[3]+B[1]\leq A[3]+B[2]\leq A[3]+B[3]\leq\) ...
  4. ......

这里用优先队列维护一个二元组 \((s,b)\) ,其中,\(s=A[a]+B[b]\)\(s\) 最小的放堆顶
每次从堆中取出一个 \((s,b)\) 后,将 \((s',b+1)\) 丢入堆中,直到取出了 \(n\) 个最小值
但是我们在维护 \((s,b)\) 这个二元组时并没有记录 \(a\) 的值,那则么样才能得到 \((s',b+1)\)
很简单, \(s'=s-B[b]+B[b+1]\),根本不需要知道 \(a\)
$
$
这道题将上面那个两个序列的情况进行了推广
其实就相当于每次拿一个序列去跟 \(A序列\) 进行上面那个操作,然后用答案覆盖 \(A\),直到所有序列都操作过一次

//made by Hero_of_Someone
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<queue>
#include<algorithm>
#define il inline
#define RG register
using namespace std;
il int gi(){ RG int x=0,q=1; RG char ch=getchar(); while( ( ch<'0' || ch>'9' ) && ch!='-' ) ch=getchar();
  if( ch=='-' ) q=-1,ch=getchar(); while(ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar(); return q*x; }

int n,a[1010][1010];
struct node{int s,x;
   il bool operator<(const node &c)const{return s>c.s;}
};
priority_queue<node>q;

il void init(){
   for(int i=1;i<=n;i++){
      for(int j=1;j<=n;j++)
         a[i][j]=gi();
      sort(a[i]+1,a[i]+n+1);
   }
}

il void work(){
   for(int i=2;i<=n;i++){
      for(int j=1;j<=n;j++)
         q.push((node){a[1][j]+a[i][1],1});
      for(int j=1;j<=n;j++){
         node x=q.top(); q.pop();
         a[1][j]=x.s; int b=x.x;
         if(b<n) q.push((node){x.s-a[i][b]+a[i][b+1],b+1});
      }
      while(!q.empty()) q.pop();
   }
   printf("%d",a[1][1]); for(int i=2;i<=n;i++) printf(" %d",a[1][i]);
   puts("");
}

int main(){ while(scanf("%d",&n)!=EOF){ init(); work(); } return 0; }

转载于:https://www.cnblogs.com/Hero-of-someone/p/7683794.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值