K个最小和【优先队列】
题目大意
有k个整数数组,各包含k个元素。在每个数组中取一个元素加起来。可以得到kk个和。求这些和中最小的k个值(重复的值算多次)
输入格式
输入包含多组数据。每组数据第一行为一个整数k(2≤k≤750)。以下k行每行包含k个不超过106。输入结束标志位文件结束符(EOF)。输入文件不超过5MB.
输出格式
对于每组数据,输出k个最小和的值,并按照从小到大的顺序排序。
题目分析
若只有两列数 则有 n2种情况
那么,我们要取前k种
可以根据 将其构成 n张 有序表
a,b都有序的情况下
a[1]+b[1]≤a[1]+b[2]≤...≤a[1]+b[n]
a[2]+b[1]≤a[2]+b[2]≤...≤a[2]+b[n]
a[n]+b[1]≤a[n]+b[2]≤...≤a[n]+b[n]
而 已知 a[1]+b[1] =s
s’ = a[1]+[b2] = s-b[1]+b[2]
因此,我们直接将 a[i]+b[1] ( 1≤i≤n)放入优先队列中 ,然后递推得出 后面的项。不断放入优先队列里。取出前k项即可。
但是这样我们有可能会将第二个数组的中数值加多次,但是,我们可以证明
a[i]+b[x]+b[y]≤a[i]+b[x],a[i]+b[x]+b[y]≤a[i]+b[y]
这两个式子恒成立。
所以 当 k≤n时,一定不会取到 a[i]+b[x]+b[y]
然而目前,有 n列数
那么只要每次合并两个,合并n次即可
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1000;
int a[maxn][maxn];
int ans[maxn];
int n;
struct node
{
int s,k;
bool operator<(const node&oth)const
{
return oth.s<s;
}
node(int _s,int _k):s(_s),k(_k){}
};
void getans(int t)
{
//cout<<"round = "<<t<<endl;
priority_queue<node>pq;
for(int i=1;i<=n;i++)
{
pq.push(node(a[t][1]+ans[i],1));
}
for(int i=1;i<=n;i++)
{
node tmp = pq.top();
pq.pop();
ans[i]=tmp.s;
//cout<<tmp.s<<" "<<tmp.k<<endl;
tmp.s = tmp.s-a[t][tmp.k]+a[t][tmp.k+1];
//cout<<"s= "<<tmp.s<<endl;
if(tmp.k+1<=n) {
//cout<<"push in"<<tmp.s<<" "<<tmp.k+1<<endl;
pq.push(node(tmp.s,tmp.k+1));
}
//cout<<ans[i]<<" "<<endl;
}
//cout<<endl;
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
memset(a,0,sizeof(a));
memset(ans,0,sizeof(ans));
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
scanf("%d",&a[i][j]);
}
sort(a[i]+1,a[i]+1+n);
}
for(int i=1;i<=n;i++) ans[i]=a[1][i];
for(int i=2;i<=n;i++)
getans(i);
for(int i=1;i<=n;i++)
{
if(i==n) printf("%d\n",ans[i]);
else printf("%d ",ans[i]);
}
}
return 0;
}