Description
【问题描述】
从n个数中选若干(至少1)个数求和,求所有方案中第k小的和(和相同但取法不同的视为不同方案)。
【输入格式】
第一行输入2个正整数n,k。
第二行输入这n个正整数。
【输出格式】
输出第k小的和。
【样例输入】
5 12
1 2 3 5 8
【样例输出】
8
【样例解释】
前12小的和分别为:1 2 3 3 4 5 5 6 6 7 8 8
【数据规模和约定】
测试点1,n<=16。
测试点2-3,n<=100,k<=500。
测试点4-5,n<=1000,k<=5000。
测试点6-8,n<=10^5,k<=5*10^5。
测试点9-10,n<=35。
对于所有数据,1<=k<2^n,n个正整数每个都不超过10^9。
对于测试点1-8可采用DFS方式:
对于原序列中数排序,之后每个数可采取两种选择 将1记为选 0记为不选
那么可构造出一棵搜索树
但直接搜索状态数过多 且有许多无用状态(0->0、0->0、0、0)
所以可以优化
用二元组(sum,i)表示和为sum,当前考虑第i个位置是否要选。
先将二元组(a[1],2)放入队列 之后每次取队首元素 并拓展为(val-a[pos-1]+a[pos],pos+1),拓展k次可得正确答案 状态数为2k+1;
例如:
对于状态(a[1],2)可拓展为(a[2],3)和(a[1]+a[2],3)避免了重复且排除了无用状态
时间复杂度O(KlogK)
80分代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
const int Maxn=1e6+50;
inline int read()
{
char ch=getchar();int i=0,f=1;
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){i=(i<<3)+(i<<1)+ch-'0';ch=getchar();}
return i*f;
}
struct node
{
ll val;
int pos;
node(ll val,int pos):val(val),pos(pos){}
friend inline bool operator <(const node &a,const node &b)
{
return a.val>b.val;
}
};
priority_queue<node>q;
ll a[Maxn];
int n,k;
inline void readin()
{
n=read(),k=read();
for(int i=1;i<=n;i++)a[i]=read();
sort(a+1,a+n+1);
}
inline void solve()
{
q.push(node(a[1],2));
for(int i=1;i<k;i++)
{
ll val=q.top().val;
int nowpos=q.top().pos;
q.pop();
if(nowpos<=n)
{
q.push(node(val-a[nowpos-1]+a[nowpos],nowpos+1));
q.push(node(val+a[nowpos],nowpos+1));
}
}
printf("%lld",q.top().val);
}
int main()
{
readin();
solve();
}
注意long long ...
100分代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
const int Maxn=1e7+50;
inline ll read()
{
char ch=getchar();ll i=0,f=1;
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){i=(i<<3)+(i<<1)+ch-'0';ch=getchar();}
return i*f;
}
struct node
{
ll val;
int pos;
node(ll val,int pos):val(val),pos(pos){}
friend inline bool operator <(const node &a,const node &b)
{
return a.val>b.val;
}
};
priority_queue<node>q;
ll a[Maxn];
int n,cnta,cntb;
ll ansa[Maxn],ansb[Maxn],k;
inline void readin()
{
n=read(),k=read();
for(int i=1;i<=n;i++)a[i]=read();
sort(a+1,a+n+1);
}
inline void solve()
{
q.push(node(a[1],2));
for(int i=1;i<k;i++)
{
ll val=q.top().val;
int nowpos=q.top().pos;
q.pop();
if(nowpos<=n)
{
q.push(node(val-a[nowpos-1]+a[nowpos],nowpos+1));
q.push(node(val+a[nowpos],nowpos+1));
}
}
printf("%lld\n",q.top().val);
}
inline void Dfs1(int now,int max,int &cnt,ll sum)
{
if(now>max){if(sum)ansa[++cnt]=sum;}
else
{
Dfs1(now+1,max,cnt,sum+a[now]);
Dfs1(now+1,max,cnt,sum);
}
}
inline void Dfs2(int now,int max,int &cnt,ll sum)
{
if(now>max){if(sum)ansb[++cnt]=sum;}
else
{
Dfs2(now+1,max,cnt,sum+a[now]);
Dfs2(now+1,max,cnt,sum);
}
}
inline bool judge(ll val)
{
ll ret=0;
int j=1;
for(int i=cnta;i>=0;i--)
{
if(ansa[i]>val)continue;
for(;j<=cntb&&ansb[j]+ansa[i]<=val;j++);
ret+=j;
}
return ret>k;
}
inline void solve2()
{
Dfs1(1,n/2,cnta,0);
Dfs2(n/2+1,n,cntb,0);
sort(ansa+1,ansa+cnta+1);
sort(ansb+1,ansb+cntb+1);
ll l=0,r=0x3f3f3f3f3f3f3f3f,mid;
while(l<=r)
{
mid=(l+r)>>1;
if(judge(mid))r=mid-1;
else l=mid+1;
}
printf("%lld\n",r+1);
}
int main()
{
readin();
if(n>35)solve();
else solve2();
}