抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

别看他简单,但是代码打起来特别容易红温,而且容易想不到漏洞。特别是大模拟题……

本文简要总结一下常用的搜索算法:

二分搜索

使用二分搜索的前提条件是每种情况的答案具有二分性————即大于某一点符合某一性质,小于等于某一点则不符合某一性质。一般常和排序一起用。

例题:2020 ICPC Shanghai Site D.Walker

首先把特殊情况处理一下:一个人走完全程、两人相对走到终点。

其次列举某点,某一人走完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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#ifndef VLSMB_VS
#include <bits/stdc++.h>
#else
#include <iostream>
#include <iomanip>
#include <set>
#include <algorithm>
#include <map>
#include <vector>
#include <stack>
#endif

#define int long long
#define pii pair<int,int>
#define endl '\n'
#define veci vector<int>
#define vecpi vector<pii >
#define mpi map<int,int>
#define mpc map<char,int>
#define inf 0x7fffffffffffffff
#define eps 1e-8
#define yes cout<<"yes"<<endl
#define no cout<<"no"<<endl
#define seti set<int>
#define mulseti multiset<int>
#define setpi set<pii >
#define priqli priority_queue<int,veci,less<int> >
#define priqgi priority_queue<int,veci,greater<int> >
using namespace std;
const int N = 1000000;
void solve();
void init();

signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t=1;
init();
cin>>t;
while(t--)solve();
cout.flush();
return 0;
}

void solve()
{
double n,p1,v1,p2,v2;
cin>>n>>p1>>v1>>p2>>v2;
if(p1>p2)swap(p1,p2),swap(v1,v2);
double ans=min((min(n-p1,p1)+n)/v1,(min(n-p2,p2)+n)/v2);
ans=min(ans,max((n-p1)/v1,p2/v2));
double l=p1,r=p2;
while(r-l>eps)
{
double mid=(l+r)/2;
double t1=(min(p1,mid-p1)+mid)/v1;
double t2=(min(n-p2,p2-mid)+(n-mid))/v2;
if(t1>t2)r=mid;
else l=mid;
ans=min(ans,max(t1,t2));
}
cout<<setprecision(10)<<ans<<endl;
}
void init()
{

}

二进制枚举

常用于个数较少(N<=20)时直接用,i的每一二进制位代表选择或者不选择某一操作。

POJ1753

思路:直接翻

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#ifdef VLSMB_VS

#else
#include <iostream>
#include <iomanip>
#include <set>
#include <algorithm>
#include <map>
#include <vector>
#include <stack>
#endif

#define int long long
#define pii pair<int,int>
#define endl '\n'
#define veci vector<int>
#define vecpi vector<pii >
#define mpi map<int,int>
#define mpc map<char,int>
#define inf 0x7fffffffffffffff
#define eps 1e-8
#define yes cout<<"yes"<<endl
#define no cout<<"no"<<endl
#define seti set<int>
#define mulseti multiset<int>
#define setpi set<pii >
#define priqli priority_queue<int,veci,less<int> >
#define priqgi priority_queue<int,veci,greater<int> >
using namespace std;
const int N = 1000000;
void solve();
void init();

signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t=1;
init();
//cin>>t;
while(t--)solve();
cout.flush();
return 0;
}
char tab[10][10];
char tmp[10][10];
int d[4][2]={{0,1},{1,0},{0,-1},{-1,0}};

void solve()
{
int ans=200;
for(int i=0;i<4;i++)for(int j=0;j<4;j++)cin>>tab[i][j];
for(int s=0;s<(1<<16);s++)
{
for(int i=0;i<4;i++)for(int j=0;j<4;j++)tmp[i][j]=tab[i][j];

for(int i=0;i<4;i++)
{
for(int j=0;j<4;j++)
{
if(s&(1ull<<(4*i+j)))
{
if(tmp[i][j]=='b')tmp[i][j]='w';
else tmp[i][j]='b';
for(int k=0;k<4;k++)
{
if(i+d[k][0]>=0&&i+d[k][0]<4&&j+d[k][1]>=0&&j+d[k][1]<4)
{
if(tmp[i+d[k][0]][j+d[k][1]]=='b')tmp[i+d[k][0]][j+d[k][1]]='w';
else tmp[i+d[k][0]][j+d[k][1]]='b';
}
}
}
}
}
bool flag=true;
for(int i=0;i<4&&flag;i++)for(int j=0;j<4&&flag;j++)flag=tmp[i][j]==tmp[0][0];
if(flag)
{
int cnt=0;
int tx=s;
while(tx)
{
if(tx&1)cnt++;
tx>>=1;
}
ans=min(ans,cnt);
}
}
if(ans!=200)cout<<ans<<endl;
else cout<<"Impossible"<<endl;
}
void init()
{

}

DFS深度优先搜索

用于可能需要回溯剪枝的,注意需要一个vis辅助数组代表上一层所选的元素。

HD1016 Prime Ring Problem

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#ifndef VLSMB_VS
#include <bits/stdc++.h>
#else
#include <iostream>
#include <iomanip>
#include <set>
#include <algorithm>
#include <map>
#include <vector>
#include <stack>
#endif

#define int long long
#define pii pair<int,int>
#define endl '\n'
#define veci vector<int>
#define vecpi vector<pii >
#define mpi map<int,int>
#define mpc map<char,int>
#define inf 0x7fffffffffffffff
#define eps 1e-8
#define yes cout<<"yes"<<endl
#define no cout<<"no"<<endl
#define seti set<int>
#define mulseti multiset<int>
#define setpi set<pii >
#define priqli priority_queue<int,veci,less<int> >
#define priqgi priority_queue<int,veci,greater<int> >
using namespace std;
const int N = 1000000;
void solve();
void init();

signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t=1;
init();
//cin>>t;
while(t--)solve();
cout.flush();
return 0;
}
mpi prime;
void dfs(veci&ans,veci&vis,int n,int d)
{
if(d==n)
{
if(!prime[ans[0]+ans[n-1]])return;
for(const auto&x:ans)cout<<x<<' ';
cout<<endl;
return;
}
for(int i=2;i<=n;i++)
{
if(!vis[i]&&prime[ans[d-1]+i])
{
ans.push_back(i);
vis[i]=1;
dfs(ans,vis,n,d+1);
ans.pop_back();
vis[i]=0;
}
}
}
void solve()
{
int t=1;
int n;
while(cin>>n)
{
cout<<"Case "<<t<<':'<<endl;
t++;
veci ans;
veci vis(n+1);
vis[1]=1;
ans.push_back(1);
dfs(ans,vis,n,1);
cout<<endl;
}
}
void init()
{
prime[2]++;
prime[3]++;
prime[5]++;
prime[7]++;
prime[11]++;
prime[13]++;
prime[17]++;
prime[19]++;
prime[23]++;
prime[29]++;
prime[31]++;
prime[37]++;
prime[41]++;
prime[43]++;
}

BFS广度优先搜索

常见于走迷宫问题,一样需要一个vis辅助数组标记已经访问过的节点。注意有多组数据的时候要对vis数组进行清空处理。常用队列模拟每个节点走过的途径。题目越ex队列要存的东西越复杂。有些题可能需要一个状态辅助参数,有些节点需要某种状态才能进入之类的,如果状态较少可以直接用二进制数字保存。

HD1429 胜利大逃亡(续)

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#ifdef VLSMB_VS
#include <bits/stdc++.h>
#else
#include <iostream>
#include <iomanip>
#include <set>
#include <algorithm>
#include <map>
#include <vector>
#include <stack>
#include <queue>
#endif

#define int long long
#define pii pair<int,int>
#define endl '\n'
#define veci vector<int>
#define vecpi vector<pii >
#define mpi map<int,int>
#define mpc map<char,int>
#define inf 0x7fffffffffffffff
#define eps 1e-8
#define yes cout<<"yes"<<endl
#define no cout<<"no"<<endl
#define seti set<int>
#define mulseti multiset<int>
#define setpi set<pii >
#define priqli priority_queue<int,veci,less<int> >
#define priqgi priority_queue<int,veci,greater<int> >
using namespace std;
const int N = 1000000;
void solve();
void init();
int gcd(int a,int b)
{
return b?gcd(b,a%b):a;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t=1;
init();
//cin>>t;
while(t--)solve();
cout.flush();
return 0;
}
char table[25][25];
int d[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
int vis[25][25][2000];

void solve()
{
int n,m,t;
while(cin>>n>>m>>t)
{
for(int i=0;i<25;i++)for(int j=0;j<25;j++)for(int k=0;k<2000;k++)vis[i][j][k]=0;
pii org;
int has=0;
map<pii, int>h1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
cin>>table[i][j];
if(table[i][j]=='@')org.first=i,org.second=j;
if(table[i][j]=='*')has++,h1[{i,j}]++;
}
}
// tmp1为当前坐标,tmp2为时间和10个bit状态位
queue<pair<pii,pii > >q;
q.push({org,{0,0}});
// h[org]++;
// int has=1;

while(!q.empty())
{
if(has==n*m)break;
pii tmp1=q.front().first;
pii tmp2=q.front().second;
q.pop();
if(table[tmp1.first][tmp1.second]=='^')
{
cout<<tmp2.first<<endl;
goto succ;
}
if(!h1[tmp1])h1[tmp1]++,has++;
if(table[tmp1.first][tmp1.second]>='a'&&table[tmp1.first][tmp1.second]<='j')
{
int off=table[tmp1.first][tmp1.second]-'a';
tmp2.second |= (1ull<<off);
}
tmp2.first++;
if(tmp2.first==t)continue;
for(int i=0;i<4;i++)
{
if(!(tmp1.first+d[i][0]>=1&&tmp1.first+d[i][0]<=n&&tmp1.second+d[i][1]>=1&&tmp1.second+d[i][1]<=m))
continue;
if(table[tmp1.first+d[i][0]][tmp1.second+d[i][1]]=='*')continue;
if(table[tmp1.first+d[i][0]][tmp1.second+d[i][1]]=='^'||table[tmp1.first+d[i][0]][tmp1.second+d[i][1]]=='@'||table[tmp1.first+d[i][0]][tmp1.second+d[i][1]]=='.' || (table[tmp1.first+d[i][0]][tmp1.second+d[i][1]]>='a'&&table[tmp1.first+d[i][0]][tmp1.second+d[i][1]]<='j')
|| (table[tmp1.first+d[i][0]][tmp1.second+d[i][1]]>='A'&&table[tmp1.first+d[i][0]][tmp1.second+d[i][1]]<='J'&&(tmp2.second&(1ull<<(table[tmp1.first+d[i][0]][tmp1.second+d[i][1]]-'A'))) ) )
{
if(!vis[tmp1.first+d[i][0]][tmp1.second+d[i][1]][tmp2.second])
{
q.push({{tmp1.first+d[i][0],tmp1.second+d[i][1]},tmp2});
vis[tmp1.first+d[i][0]][tmp1.second+d[i][1]][tmp2.second]++;
}
}
}
}
cout<<-1<<endl;
succ:
continue;
}
}
void init()
{

}

双向搜索

对于数据量较大的BFS,可以考虑从起点和终点双向搜索,或分区间搜索。每一次处理一般的数据,最后再将答案合并。

ATCoder abc271F XOR on Grid Path

这题思路为从起点到终点进行dfs,以i+j==n+1为界限,看两次dfs的结果。在边界线上的每一个点,值相等的记录数相乘。对每个点求和即为答案。

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#ifndef VLSMB_VS
#include <bits/stdc++.h>
#else
#include <iostream>
#include <iomanip>
#include <set>
#include <algorithm>
#include <map>
#include <vector>
#include <stack>
#include <queue>
#include <deque>
#include <string>
#include <cstdio>
#include <cmath>
#endif

#define int long long
#define pii pair<int,int>
#define endl '\n'
#define veci vector<int>
#define vecpi vector<pii >
#define mpi map<int,int>
#define mpc map<char,int>
#define inf 0x7fffffffffffffff
#define eps 1e-8
#define yes cout<<"yes"<<endl
#define no cout<<"no"<<endl
#define seti set<int>
#define mulseti multiset<int>
#define setpi set<pii >
#define priqli priority_queue<int,veci,less<int> >
#define priqgi priority_queue<int,veci,greater<int> >
using namespace std;
const int N = 1000000;
void solve();
void init();

signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t=1;
init();
//cin>>t;
while(t--)solve();
cout.flush();
return 0;
}
int a[30][30];

mpi h1[30][30],h2[30][30];
void dfs1(int i,int j,int n,int val)
{
if(i+j==n+1)
{
h1[i][j][val]++;
return;
}
if(i<n)dfs1(i+1,j,n,val^a[i][j]);
if(j<n)dfs1(i,j+1,n,val^a[i][j]);
}
void dfs2(int i,int j,int n,int val)
{
if(i+j==n+1)
{
h2[i][j][val]++;
return;
}
if(i>1)dfs2(i-1,j,n,val^a[i-1][j]);
if(j>1)dfs2(i,j-1,n,val^a[i][j-1]);
}
void solve()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)cin>>a[i][j];
int ans=0;
dfs1(1,1,n,0);
dfs2(n,n,n,a[n][n]);
for(int i=1;i<=n;i++)
{
for(const auto&p:h1[i][n+1-i])ans+=p.second*h2[i][n+1-i][p.first];
}
cout<<ans<<endl;
}
void init()
{

}

暴力循环

其实有的题写三个for就能过……但一般都要对表达式进行变形,你才能看出来。

SPOJ ABCDEF

比如说这题,写5个循环肯定寄,写4个循环也会被卡。我们变形一下,可以把e移到右面,再将d乘过去。注意枚举的时候d不能为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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#ifndef VLSMB_VS
#include <bits/stdc++.h>
#else
#include <iostream>
#include <iomanip>
#include <set>
#include <algorithm>
#include <map>
#include <vector>
#include <stack>
#include <queue>
#include <deque>
#include <string>
#include <cstdio>
#include <cmath>
#endif

#define int long long
#define pii pair<int,int>
#define endl '\n'
#define veci vector<int>
#define vecpi vector<pii >
#define mpi map<int,int>
#define mpc map<char,int>
#define inf 0x7fffffffffffffff
#define eps 1e-8
#define yes cout<<"yes"<<endl
#define no cout<<"no"<<endl
#define seti set<int>
#define mulseti multiset<int>
#define setpi set<pii >
#define priqli priority_queue<int,veci,less<int> >
#define priqgi priority_queue<int,veci,greater<int> >
using namespace std;
const int N = 1000000;
void solve();
void init();
int gcd(int a,int b)
{
return b?gcd(b,a%b):a;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t=1;
init();
//cin>>t;
while(t--)solve();
cout.flush();
return 0;
}

void solve()
{
int n;
cin>>n;
veci s(n);
int ans=0;
for(auto&x:s)cin>>x;
mpi h1,h2;
for(int i=0;i<n;i++)for(int j=0;j<n;j++)for(int k=0;k<n;k++)
{
h1[s[i]*s[j]+s[k]]++;
if(s[i])h2[s[i]*(s[j]+s[k])]++;
}
for(const auto&p:h1)ans+=p.second*h2[p.first];
cout<<ans<<endl;
}
void init()
{

}

BFS + 优先队列

POJ2908 Quantum

有的时候直接BFS队列取出来的第一个答案不一定是最优解。如POJ2908,测试点为

1
2
3
4
5
6
1
4 3 1
CCCC 10
NNSS 1
SSNN 1
0000 1111

正确答案应该是2,但是使用普通队列的话,取出第一轮的答案之后我们直接输出答案10是错误的,等队列为空取最小值还会超时。因此我们可以用优先队列,让价值最小的优先出队,不管它现在是第几轮的操作。

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#ifdef VLSMB_VS

#else
#include <iostream>
#include <iomanip>
#include <set>
#include <algorithm>
#include <map>
#include <vector>
#include <stack>
#include <queue>
#include <deque>
#include <string>
#include <cstdio>
#include <cmath>
#endif

#define int unsigned long long
#define pii pair<int,int>
#define endl '\n'
#define veci vector<int>
#define vecpi vector<pii >
#define mpi map<int,int>
#define mpc map<char,int>
#define inf 0x7fffffffffffffff
#define inf3f 0x3f3f3f3f3f3f3f3f
#define eps 1e-8
#define yes cout<<"yes"<<endl
#define no cout<<"no"<<endl
#define seti set<int>
#define mulseti multiset<int>
#define setpi set<pii >
#define priqli priority_queue<int,veci,less<int> >
#define priqgi priority_queue<int,veci,greater<int> >
#define priqlpi priority_queue<pii,vecpi,less<pii> >
#define priqgpi priority_queue<pii,vecpi,greater<pii > >
using namespace std;
const int N = 1000000;
void solve();
void init();

signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t=1;
init();
cin>>t;
while(t--)solve();
cout.flush();
return 0;
}
int vis[1ull<<20],dis[1ull<<20];
bool operator<(const pair<int,string>&a,const pair<int,string>&b)
{
return a.first<b.first;
}
struct node
{
int first,second;
node(int x,int y):first(x),second(y){}
};
bool operator<(const node&a,const node&b)
{
return a.first>b.first;
}
void solve()
{
int l,n,m;
cin>>l>>n>>m;
vector<pair<int,string> >op(n);
for(int i=0;i<n;i++)cin>>op[i].second>>op[i].first;
// sort(op.begin(),op.end());
while(m--)
{
for(int i=0;i<(1ull<<l);i++)vis[i]=0,dis[i]=inf3f;
string src,dest;
cin>>src>>dest;
int s=0,t=0;
for(int i=0;i<l;i++)
{
if(src[i]=='1')s|=1ull<<(l-1-i);
if(dest[i]=='1')t|=1ull<<(l-1-i);
}
priority_queue<node > q;
node tmp(0,s);
q.push(tmp);
// vis[s]++;
dis[s]=0;
int ans=inf;
while(!q.empty())
{
int cost=q.top().first;
int cur=q.top().second;
const int sub=cur;
q.pop();
vis[cur]++;
if(cur==t)
{
cout<<cost;
if(m)cout<<' ';
goto succ;
}
// for(const auto&p:op)
for(vector<pair<int,string> >::iterator p=op.begin();p!=op.end();p++)
{
cur=sub;
const string&chg=p->second;
for(int i=0;i<l;i++)
{
if(chg[i]=='F')cur^=1ull<<(l-i-1);
else if(chg[i]=='S')cur|=1ull<<(l-i-1);
else if(chg[i]=='C'&&(cur&(1ull<<(l-i-1))))cur^=1ull<<(l-i-1);
}
node tmp2(cost+p->first,cur);
if(!vis[cur]&&dis[cur]>cost+p->first)dis[cur]=cost+p->first,q.push(tmp2);
}
}
cout<<"NP";
if(m)cout<<' ';
succ:
continue;
}
cout<<endl;
}
void init()
{

}