题意简述
一个大小为 的方阵,每行都选一个数,得到一个和。一共能得到 个和。求出最小的前 个。重复的算多次。 。
思路框架
每两行都来一个多路归并即珂。
具体思路
今天去看了刘汝佳的小蓝书。感谢刘汝佳,让我知道优先队列原来还能这么用。
首先,每行里面顺序不重要,先排序再说。
前置知识:n路归并问题
我们应该都知道“二路归并”问题:两个有序的序列,要合并成一个有序的序列。解法很简单,维护两个指针,哪个小就把哪个加入答案,然后让指针++。利用它,我们珂以写出大名鼎鼎的「归并排序」算法。
路归并问题就是: 个序列,每个序列都是有序的。要合并成一个长度为 的有序的序列。
但是我们发现,我们要维护 个指针,一次维护指针是 的,我们一共有 的元素,就变成 了。怎么样更快呢?我们珂以用优先队列来找到值最小的那个指针。这样维护一次指针是 的,总共就是 的了。而且原问题中我们只需要求前面的 个,所以这会更快,变成每次 的时间。
如何转化
我们的原问题是有 行 个和的,如何转化?
首先我们珂以每两行之间逐个合并。然后对于两行
和
的问题,我们只需要生成这样
个序列即珂:
第一个:
。
第二个:
。
…
第
个:
。
然后跑一个
路归并即珂。这样做
次,每次都只求前面
个小的,我们就珂以求出
个和中最小的
个了。
实现细节:关于如何维护指针
如果您足够强,您完全不用看这个。
我们维护两个值, 。 表示目前的 , 表示在 中的下标。我们完全不用知道 中的下标,因为 是不会改变的。我们有 ,下一个指针 其实就是 。
在优先队列中,我们显然是要以 为关键字判断优先级的。
—— 刘汝佳的思路
代码
#include <bits/stdc++.h>
using namespace std;
namespace Flandre_Scarlet
{
#define N 822
#define F(i,l,r) for(int i=l;i<=r;++i)
#define D(i,r,l) for(int i=r;i>=l;--i)
#define Fs(i,l,r,c) for(int i=l;i<=r;c)
#define Ds(i,r,l,c) for(int i=r;i>=l;c)
#define MEM(x,a) memset(x,a,sizeof(x))
#define FK(x) MEM(x,0)
#define Tra(i,u) for(int i=G.Start(u),__v=G.To(i);~i;i=G.Next(i),__v=G.To(i))
#define p_b push_back
#define sz(a) ((int)a.size())
#define iter(a,p) (a.begin()+p)
void R1(int &x)
{
x=0;char c=getchar();int f=1;
while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=(f==1)?x:-x;
}
int n;
int a[N][N];
void Input()
{
F(i,1,n) F(j,1,n) R1(a[i][j]);
F(i,1,n) sort(a[i]+1,a[i]+n+1);
}
struct node{int s,b;};bool operator<(node a,node b){return a.s>b.s;}
//用一个struct维护指针,排序条件是以s为关键字的
priority_queue<node> Q;
void Merge(int a[N],int b[N],int c[N]) //A和B序列中生成的n^2个和中,排序之后前面n个存在C数组中
//Merge: O(nlogn)/次
{
while(!Q.empty()) Q.pop(); //勿忘清空啊
F(i,1,n) Q.push((node){a[i]+b[1],1}); //队列的初始状态
F(i,1,n) //只求前面n个,i循环到n即珂
{
node Min=Q.top();Q.pop();
c[i]=Min.s;
Q.push((node){Min.s-b[Min.b]+b[Min.b+1],Min.b+1}); //求出下一个指针
}
}
void Soviet()
{
F(i,2,n)
{
Merge(a[1],a[i],a[1]); //不断合并,合并的结果都放在a[1]里面
} //总共O(n^2logn)
F(i,1,n) printf("%d%c",a[1][i]," \n"[i==n]);
}
#define Flan void
Flan IsMyWife()
{
while(~scanf("%d",&n) and n)
{
Input();
Soviet();
}
}
}
int main()
{
Flandre_Scarlet::IsMyWife();
getchar();getchar();
return 0;
}