BJ模拟 第k小和(DFS+二分+折半)

来源:互联网 发布:淘宝市场行情标准版 编辑:程序博客网 时间:2024/05/16 23:56

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();}


剩下20%数据:二分答案以后折半搜索,时间复杂度O(2^{K/2}logW)

注意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();}



0 0