poj 折半搜索
Posted dlutjwh
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了poj 折半搜索相关的知识,希望对你有一定的参考价值。
poj2549 Sumsets
题目链接: http://poj.org/problem?id=2549
题意:给你一个含有n(n<=1000)个数的数列,问这个数列中是否存在四个不同的数a,b,c,d,使a+b+c=d;若存在则输出最大的d
思路:完全暴力的话O(n^4),会T,可以考虑双向搜索,公式变形为a+b=d-c;分别枚举a+b和c-d,将值和下标存在结构体中,再二分查找即可
#include<cstdio> #include<iostream> #include<algorithm> #include<set> #include<vector> #include<map> using namespace std; struct Z{ int s; int x; int y; bool operator < (const Z& b)const { return s < b.s; } }z[1000005],m[1000005]; int n,a[1005]; bool ok(Z& a, Z& b) { return a.x != b.x && a.y != b.y && a.x != b.y && a.y != b.x; } int main(){ while((cin>>n)&&n!=0){ int ans=-536870912; for(int i=0;i<n;i++) cin>>a[i]; sort(a,a+n); int k=0; for(int i=0;i<n;i++) for(int j=i+1;j<n;j++){ z[k].s=a[i]+a[j]; z[k].x=i; z[k++].y=j; } int l=0; for(int i=0;i<n;i++) for(int j=i+1;j<n;j++){ m[l].s=a[i]-a[j]; m[l].x=i; m[l++].y=j; m[l].s=a[j]-a[i]; m[l].x=j; m[l++].y=i; } sort(z,z+k); for(int i=0;i<l;i++){ int d=lower_bound(z,z+k,m[i])-z; if(ok(z[d],m[i])&&z[d].s==m[i].s) ans=max(ans,z[d].s+a[m[i].y]); } if(ans!=-536870912) cout<<ans<<endl; else cout<<"no solution"<<endl; } }
poj3977 Subset
题目链接: http://poj.org/problem?id=3977
题意:给你一个含n(n<=35)个数的数组,让你在数组中选出一个非空子集,使其元素和的绝对值最小,输出子集元素的个数以及元素和的绝对值,若两个子集元素和相等,输出元素个数小的那个。
#include<cstdio> #include<iostream> #include<algorithm> #include<cmath> using namespace std; struct Z{ long long int x; int y; bool operator < (const Z& b)const { if(x!=b.x) return x < b.x; return y<b.y; } }a[300005],b[300005]; long long int c[40]; long long int abs1(long long int x){ if(x<0) return -x; return x; } int main(){ int n; while((cin>>n)&&n!=0){ for(int i=0;i<300005;i++) a[i].x=a[i].y=b[i].x=b[i].y=0; long long int sum=1e17; int ans=40; for(int i=0;i<n;i++) cin>>c[i]; int n1=n/2; for(int i=0;i<1<<n1;i++){ for(int j=0;j<n1;j++){ if(i>>j&1&&(i!=0||j!=0)){ a[i-1].x+=c[j]; a[i-1].y++; } } } int n2=n-n1; for(int i=0;i<(1<<n2);i++){ for(int j=0;j<n2;j++){ if(i>>j&1&&(i!=0||j!=0)){ b[i-1].x+=c[j+n1]; b[i-1].y++; } } } for(int i=0;i<(1<<n1)-1;i++){ if(abs1(a[i].x)<sum){ sum=abs1(a[i].x); ans=a[i].y; } else if(abs1(a[i].x)==sum&&a[i].y<ans){ ans=a[i].y; sum=abs1(a[i].x); } } for(int i=0;i<(1<<n1)-1;i++) a[i].x=-a[i].x; for(int i=0;i<(1<<n2)-1;i++){ if(abs1(b[i].x)<sum){ sum=abs1(b[i].x); ans=b[i].y; } else if(abs1(b[i].x)==sum&&b[i].y<ans){ ans=b[i].y; sum=abs1(b[i].x); } } sort(a,a+(1<<n1)-1); sort(b,b+(1<<n2)-1); for(int i=0;i<(1<<n1)-1;i++){ int t=lower_bound(b,b+(1<<n2)-1,a[i])-b; if(t>0){ if(abs1(b[t-1].x-a[i].x)<sum){ sum=abs1(b[t-1].x-a[i].x); ans=b[t-1].y+a[i].y; } else if(abs1(b[t-1].x-a[i].x)==sum&&b[t-1].y+a[i].y<ans){ sum=abs1(b[t-1].x-a[i].x); ans=b[t-1].y+a[i].y; } } if(t<(1<<n2)-1){ if(abs1(b[t].x-a[i].x)<sum){ sum=abs1(b[t].x-a[i].x); ans=b[t].y+a[i].y; } else if(abs1(b[t].x-a[i].x)==sum&&b[t].y+a[i].y<ans){ sum=abs1(b[t].x-a[i].x); ans=b[t].y+a[i].y; } } } cout<<sum<<" "<<ans<<endl; } }
poj2785 4 Values whose Sum is 0
题目链接: http://poj.org/problem?id=2785
挑战P160
题意:给你各有n个整数的四个数组,问从每个数列中取出一个数使四个数之和为0,问共有多少种取法,一个数列中有多个相同的数字时,将他们当成不同的数字看待,1<=n<=4000;
思路:完全暴力枚举O(n^4)肯定会超时,可以考虑拆成两半后枚举设四个数a+b+c+d=0,先将a+b所有组合存在一个数组中并排序O(n^2),再枚举c+d(O(n^2)),对于每个c+d,在a+b的数列中二分搜索-(c+d),总复杂度O(n^2*log n);
直接贴书上代码:
#include<iostream> #include<algorithm> #include<cstdio> using namespace std; int a[4005],b[4005],c[4005],d[4005],cd[16000005]; int main(){ int n; scanf("%d",&n); for(int i=0;i<n;i++) scanf("%d%d%d%d",&a[i],&b[i],&c[i],&d[i]); for(int i=0;i<n;i++) for(int j=0;j<n;j++) cd[i*n+j]=c[i]+d[j]; sort(cd,cd+n*n); long long ans=0; for(int i=0;i<n;i++) for(int j=0;j<n;j++){ int CD=-(a[i]+b[j]); ans+=upper_bound(cd,cd+n*n,CD)-lower_bound(cd,cd+n*n,CD); } printf("%lld ",ans); }
以上是关于poj 折半搜索的主要内容,如果未能解决你的问题,请参考以下文章