Training:搜索入门

HDU 2199:
http://acm.hust.edu.cn/vjudge/problem/visitOriginUrl.action?id=20576
输入一个 y ,求使得 8 ∗ x 4 + 7 ∗ x 3 + 2 ∗ x 2 + 3 ∗ x + 6 = y 的 x 的值, 0 ≤ x ≤ 100 。二分查找答案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include<cstdio>
#include<cmath>
using namespace std;
const double EPS=1e-5;
inline double f(double x){
return 8*x*x*x*x+7*x*x*x+2*x*x+3*x+6;
}
int main(){
int t;scanf("%d",&t);
while(t--){
double k;scanf("%lf",&k);
if(f(0)>k||f(100)<k){
printf("No solution!\n");
continue;
}
double l=0,r=100,m=50;
while(fabs(f(m)-k)>EPS){
if(f(m)>k) r=m;
else l=m;
m=(l+r)/2;
}
printf("%.4f\n",m);
}
return 0;
}

HDU 2899:
http://acm.hust.edu.cn/vjudge/problem/visitOriginUrl.action?id=20577
给出函数 F ( x ) = 6 ∗ x 7 + 8 ∗ x 6 + 7 ∗ x 3 + 5 ∗ x 2 − y ∗ x ,输入 y ,求函数最小值。
首先对该函数求导,然后二分查找使导函数为0的值 x 0 ,输出 F ( x 0 ) 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include<cstdio>
#include<cmath>
using namespace std;
const double EPS=1e-6;
double y;
inline double f(double x){
return 6*x*x*x*x*x*x*x+8*x*x*x*x*x*x+7*x*x*x+5*x*x-y*x;
}
inline double f2(double x){
return 42*x*x*x*x*x*x+48*x*x*x*x*x+21*x*x+10*x-y;
}
int main(){
int t;scanf("%d",&t);
while(t--){
scanf("%lf",&y);
double l=0,r=100,m=50;
while(fabs(f2(m))>EPS){
if(f2(m)>0) r=m;
else l=m;
m=(l+r)/2;
}
printf("%.4lf\n",f(m));
}
return 0;
}

HDU 1969:
http://acm.hust.edu.cn/vjudge/problem/visitOriginUrl.action?id=23123
给出 n 、 m ,有 n 个蛋糕, m + 1 个人,每个蛋糕都可以切开,求每人分一整块蛋糕的最大值。求出蛋糕大小总和,然后进行二分查找,对二分的值进行验证。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include<cstdio>
#include<cmath>
using namespace std;
const double EPS=1e-6;
const double PI=acos(-1);
const int maxn=10010;
int n,f;
double a[maxn];
inline double c(double r){
return PI*r*r;
}
bool ok(double x){
int sum=0;
for(int i=0;i<n;++i){
sum+=floor(a[i]/x);
}
return sum>=f+1;
}
int main(){
int t;scanf("%d",&t);
while(t--){scanf("%d%d",&n,&f);
double sum=0;
for(int i=0;i<n;++i){
scanf("%lf",&a[i]);
a[i]=c(a[i]);
sum+=a[i];
}
double l=0,r=sum,m=(l+r)/2;
while(r-l>EPS){
if(ok(m)) l=m;
else r=m;
m=(l+r)/2;
}
printf("%.4f\n",m);
}
return 0;
}

HDU 1905:
http://acm.hust.edu.cn/vjudge/problem/visitOriginUrl.action?id=40853
输入 p 、 a ,如果 p 不为素数且 a p % p = = a 输出yes,否则输出no。
2 < p ≤ 1000000000 , 1 < a < p
首先打出0~ p ‾ ‾ √ 的素数表,然后对每个 p 进行素性测试,再用快速幂求 a p % p 判断输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long LL;
const double EPS=1e-6;
const double PI=acos(-1);
const int maxn=100010;
int isp[maxn],pre_p;
bool np[maxn]={true,true};
void pre(){
for(int i=2;i<maxn;++i){
if(!np[i]) isp[pre_p++]=i;
for(int j=0;j<pre_p&&i*isp[j]<maxn;++j){
np[i*isp[j]]=true;
if(!(i%isp[j])) break;
}
}
return;
}
bool isprime(LL n){
double k=sqrt(n);
for(int i=0;isp[i]<=k;++i)
if(n%isp[i]==0) return false;
return true;
}
LL modexp(LL a,LL b,LL mod){
LL cur=1,tmp=a;
while(b){
if(b&1) cur=cur*tmp%mod;
tmp=tmp*tmp%mod;
b>>=1;
}
return cur;
}
int main(){
pre();
LL n,a;
while(~scanf("%lld%lld",&n,&a)&&(n||a)){
if(isprime(n)){printf("no\n");continue;}
else{
if(modexp(a,n,n)==a) printf("yes\n");
else printf("no\n");
}
}
return 0;
}

HDU 2648:
http://acm.hust.edu.cn/vjudge/problem/visitOriginUrl.action?id=18590
给出多个店的价格和每周上涨的价格,输出每周memory这家店的价格排名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include<iostream>
#include<string>
#include<map>
using namespace std;
map<string,int> a;
int main(){
int n,m,k;
while(cin>>n){
a.clear();
string s;
for(int i=0;i<n;++i) cin>>s,a[s]=0;
cin>>m;
for(int i=0;i<m;++i){
int aim=0,ans=1;
for(int j=0;j<n;++j){
cin>>k>>s;
a[s]+=k;
if(s=="memory") aim=a[s];
}
for(map<string,int>::iterator it=a.begin();it!=a.end();++it)
if(it->second>aim) ++ans;
cout<<ans<<endl;
}
}
return 0;
}

HDU 2446:
http://acm.hust.edu.cn/vjudge/problem/visitOriginUrl.action?id=11791
一开始枚举前缀和再进行二分查找。结果内存超限了。
不会优化,看了结题报告才知道可以用公式做。
http://blog.csdn.net/zxy_snow/article/details/6721066
公式: ( n 3 − n ) 6

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//原来的代码:
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn=3810779;
LL a[maxn],sum[maxn];
int main(){
for(int i=1;i<maxn;++i) a[i]=a[i-1]+i,sum[i]=sum[i-1]+a[i];
int t;scanf("%d",&t);
while(t--){
LL n;scanf("%lld",&n);
int l=0,r=0,c=0;
l=int(upper_bound(sum,sum+maxn,n)-sum);
if(sum[l-1]==n) --l;
n-=sum[l-1];
r=int(upper_bound(a,a+maxn,n)-a);
if(a[r-1]==n) --r;
c=int(n-a[r-1]);
printf("%d %d %d\n",l,r,c);
}
return 0;
}

HDU 2141:
http://acm.hust.edu.cn/vjudge/problem/visitOriginUrl.action?id=18481
给出 a 、 b 、 c 三个数组,然后输入 s ,输出是否存在 a i 、 b j 、 c k 使得 a i + b j + c k = s 。
对两个数组进行预处理,然后在另一个中枚举,在预处理后的数组中二分查找。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn=550;
LL a[maxn],b[maxn],c[maxn],k[maxn*maxn];
int main(){
int l,n,m,t=0;
while(~scanf("%d%d%d",&l,&n,&m)){
for(int i=0;i<l;++i) scanf("%lld",&a[i]);
for(int i=0;i<n;++i) scanf("%lld",&b[i]);
for(int i=0;i<m;++i) scanf("%lld",&c[i]);
int pos=0;
for(int i=0;i<l;++i)
for(int j=0;j<n;++j)
k[pos++]=a[i]+b[j];
sort(c,c+m),sort(k,k+pos);
printf("Case %d:\n",++t);
int s;scanf("%d",&s);
while(s--){
LL x;scanf("%lld",&x);
bool flag=false;
for(int i=0;i<m;++i)
if(binary_search(k,k+pos,x-c[i])){flag=true;break;}
puts(flag?"YES":"NO");
}
}
return 0;
}

HDU 2298:
http://acm.hust.edu.cn/vjudge/problem/visitOriginUrl.action?id=19904
给出物体下落点的坐标和发射初始速度,求角度。物理题推公式求根。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const double g=9.8;
const double PI=acos(-1);
int main(){
int t;scanf("%d",&t);
while(t--){
double x,y,v;
scanf("%lf%lf%lf",&x,&y,&v);
double a=g*x*x,b=-2*v*v*x,c=2*v*v*y+g*x*x,dt=b*b-4*a*c;
if(dt<0){printf("-1\n");continue;}
double x1=atan((-1*b+sqrt(dt))/(2*a)),x2=atan((-1*b-sqrt(dt))/(2*a)),ans=PI/2;
if((x1<0||x1>PI/2)&&(x2<0||x2>PI/2)){printf("-1\n");continue;}
if(x1>=0&&x1<=PI/2) ans=min(ans,x1);
if(x2>=0&&x2<=PI/2) ans=min(ans,x2);
printf("%.6f\n",ans);
}
return 0;
}

HDU 2289:
http://acm.hust.edu.cn/vjudge/problem/visitOriginUrl.action?id=15721
给出一个圆台容器,给出水的体积,求水在圆台容器中的体积。
利用圆台体积公式,二分高度,输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include<cstdio>
#include<cmath>
using namespace std;
const double PI=acos(-1);
const double EPS=1e-7;
double GetVolume(double r,double R,double h,double H)
{
double u = h/H*(R-r) + r;
return PI/3*(r*r+r*u+u*u)*h;
}
int main(){
int t;scanf("%d",&t);
while(t--){
double r,R,H,V;scanf("%lf%lf%lf%lf",&r,&R,&H,&V);
double hl=0,hr=H,h=(hl+hr)/2;
while(hr-hl>EPS){
if(GetVolume(r,R,h,H)>V) hr=h;
else hl=h;
h=(hl+hr)/2;
}
printf("%.6f\n",h);
}
return 0;
}

HDU 1010:
http://acm.hust.edu.cn/vjudge/problem/visitOriginUrl.action?id=16336
给出一个地图,问能不能在 t 步之内从起点走到终点,不能重复走过同一个格子。
因为是恰好走到,所以是DFS的题,而不是BFS。
要进行剪枝,否则会超时。首先进行奇偶性剪枝,起点终点间的距离要和 t 的奇偶性相同,然后再查找的过程中如果当前点到终点的距离小于剩余时间就回溯。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;
const int maxn=10;
const int go[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
char g[maxn][maxn];
bool vis[maxn][maxn];
int n,m,t,bx,by,ex,ey;
bool dfs(int x,int y,int cur){
if(cur==t){
if(x==ex&&y==ey) return true;
return false;
}
if(abs(x-ex)+abs(y-ey)>t-cur) return false;
for(int i=0;i<4;++i){
int a=x+go[i][0],b=y+go[i][1];
if(!vis[a][b]&&(g[a][b]=='.'||g[a][b]=='D')){
vis[a][b]=true;
if(dfs(a,b,cur+1)) return true;
vis[a][b]=false;
}
}
return false;
}
int main(){
while(~scanf("%d%d%d",&n,&m,&t)&&!(!n&&!m&&!t)){
for(int i=1;i<=n;++i){
scanf("%s",g[i]+1);
for(int j=1;j<=m;++j){
if(g[i][j]=='S') bx=i,by=j;
if(g[i][j]=='D') ex=i,ey=j;
}
}
if(abs(bx-ex)+abs(by-ey)>t||(abs(bx-ex)+abs(by-ey)+t)&1){puts("NO");continue;}
memset(vis,0,sizeof(vis));
vis[bx][by]=true;
puts(dfs(bx,by,0)?"YES":"NO");
}
return 0;
}

** 本文迁移自我的CSDN博客,格式可能有所偏差。 **