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();}
注意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
- BJ模拟 第k小和(DFS+二分+折半)
- poj3685(二分查找第k小)
- BJ模拟 Mortal Kombat(二分图匹配+tarjan)
- poj3579(二分求解第k小/大值)
- BJ模拟:stwell(BFS)
- hihocoder1133(二分求第k小)
- 【模拟】找第k小的数
- 【GDOI2018模拟8.12】区间第k小
- 【GDOI2018模拟8.12】区间第k小
- 第K小数(二分)
- 第K小数(二分)
- 有序和无序数组的二分搜索算法+select第k小的元素
- BJ模拟(2) D1T2 摩尔庄园
- BJ模拟(2) D2T2 随机游走
- BJ模拟(2) D2T3 路径规划
- BJ模拟(2) D3T1 and
- BJ模拟 医院(支配树DominatorTree)
- BJ模拟 等差数列(分块+FFT)
- [JVM]Java垃圾回收机制
- 用Python区分静态与动态网页
- Assert 和De-assert 意思
- 文件读写整理
- Android学习<AS进行APK签名>
- BJ模拟 第k小和(DFS+二分+折半)
- memcache实践以及应该注意的问题
- 【学习方法】CC2541蓝牙开发板学习方法[1]
- redis 面试总结篇
- Android SDK开发
- 【分享】导出报表中文名称乱码
- H5基础第三课时(1)
- 安全验证框架shiro(二)
- # Android(Eclipse)——拍照(打开相机)+保存到本地+显示原图(不是压缩过的)