链接
https://vjudge.net/problem/UVA-11997
题解
真是涨姿势了
首先,我可以把
k
k
k个序列的问题拆成多个
2
2
2个序列的问题来做
我可以先求前两个序列和的前
k
k
k小,然后把这
k
k
k个数当成一个新的序列,再和第三个序列这样来做
如此做的科学性在哪里呢?
假设我最后的答案表示成若干个三元组
(
a
1
,
b
1
,
c
1
)
,
(
a
2
,
b
2
,
c
2
)
.
.
.
(
a
k
,
b
n
,
c
k
)
(a_1,b_1,c_1),(a_2,b_2,c_2)...(a_k,b_n,c_k)
(a1,b1,c1),(a2,b2,c2)...(ak,bn,ck)
显然根据我的方法,任何
(
a
i
,
b
i
,
c
i
)
(a_i,b_i,c_i)
(ai,bi,ci)中的
a
i
+
b
i
a_i+b_i
ai+bi都属于前两个序列得到的前
k
k
k小,如果我把它替换成一个
a
i
+
b
i
a_i+b_i
ai+bi不是前
k
k
k小的,答案就会变得更劣
也就是说原来的答案是最优的
如此归纳下去就可以证明以上做法的正确性
然后说一下怎么处理
k
=
2
k=2
k=2的问题
直接求出
k
2
k^2
k2个和是不现实的
刘汝佳的书中给出了这样的神奇思路:
假设序列是
{
A
k
}
\{A_k\}
{Ak},
{
B
k
}
\{B_k\}
{Bk},我先使
B
i
B_i
Bi有序
然后看作
k
k
k路归并
A
1
+
B
1
≤
A
1
+
B
2
≤
⋯
≤
A
1
+
B
k
A_1+B_1 \leq A_1+B_2\leq \dots \leq A_1+B_k
A1+B1≤A1+B2≤⋯≤A1+Bk
…
\dots
…
A
k
+
B
1
≤
A
k
+
B
2
≤
⋯
≤
A
k
+
B
k
A_k+B_1 \leq A_k+B_2\leq \dots \leq A_k+B_k
Ak+B1≤Ak+B2≤⋯≤Ak+Bk
现在就成了求这
k
k
k个有序表归并得到的最终表的前
k
k
k项
可以想到先把每个表的表头存到优先级队列里去,然后取
k
k
k次,每次把后继点放进去
这种做法是基于有序性的,因为当
A
i
+
B
j
A_i+B_j
Ai+Bj还在优先队列里的时候,
A
i
+
B
j
+
1
A_i+B_{j+1}
Ai+Bj+1目前就不会影响到答案
其实只要是
k
k
k个有序序列,满足不减,而且由每一项都能推出下一项,让求最终归并得到的表的前
n
n
n个数的时候,就可以用这种方法
可以想象,这种有序序列不一定是这个题给出的这种,也可以是一个有递推式的数列(此处可出题)
代码
#include <bits/stdc++.h>
#define maxn 1010
#define linf (1ll<<60)
#define cl(x) memset(x,0,sizeof(x))
#define mkp(x,y) make_pair((x),(y))
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pr;
ll a[maxn], N, last[maxn];
void merge(ll *a, ll *b, ll *c)
{
ll i;
priority_queue< pr, vector<pr>, greater<pr> > pq;
for(i=1;i<=N;i++)
{
pq.emplace(mkp(a[i]+b[1],1));
}
for(i=1;i<=N;i++)
{
auto x=pq.top(); pq.pop();
c[i]=x.first;
if(x.second<N)pq.emplace(mkp(x.first-b[x.second]+b[x.second+1],x.second+1));
}
}
int main()
{
ll i, j;
while(cin>>N)
{
for(i=1;i<=N;i++)
{
for(j=1;j<=N;j++)scanf("%lld",a+j);
sort(a+1,a+N+1);
if(i==1)
{
memcpy(last,a,sizeof(last));
continue;
}
merge(last,a,last);
}
for(i=1;i<=N;i++)printf("%lld",last[i]), putchar(i==N?10:32);
}
return 0;
}