(POJ不好用万能头文件)
2.1搜索
递归搜索
求阶乘/斐波那契,优化使用记忆化存储数组
int memo[MAXN];
int fib(int n){
if(n<=1) return n;
if(memo[n] != 0) return memo[n];
return memo[n] = fib(n-1)+fib(n-2);
}
栈
后进先出,Last In First Out。有底的桶,只能在上边取上边放。
c++标准库中,pop/push:
#include<stack>
stack<int> mys;
mys.empty();//是否非空
mys.push(1);//{1}放入栈顶
mys.push(2);//{1,2}
mys.top();//返回栈顶
mys.pop();//{1}去除栈顶
mys.size();
stack<int> yours;
mys.swap(yours)//交换两个栈中的所有元素
队列
先进先出,First In First Out。没底的桶,上边放下边取。
c++标准库里常用:
#include<queue>
queue<int> que;
que.push(1);//{1}放入队尾
que.push(2);//{1,2}
que.front();//队首
que.pop();//{2}去除
深度优先搜索
思想是不断地转移状态,如果如法转移就退回前一步状态,直到找出最终解。用递归函数实现。
(隐约相当于栈)
例1部分和:给定整数a1, a2, …, an,选出若干数和恰好为k。
每个数决定加或不加,复杂度 O ( 2 n ) O(2^{n}) O(2n)。
int a[MAXN];
int n,k;
bool dfs(int i, int sum){
if( i==n ) return sum==k;
//不加a[i]
if( dfs(i+1, sum) ) return true;
//加a[i]
if( dfs(i, sum+a[i]) ) return true;
return false;
}
例2 POJ2386:有一大小为N*M的园子,雨后积水。八连通的积水认为连续。问有多少片水洼。‘W’表积水,’.'表陆地。(N,M<=100)
#include<cstdio> //(POJ不好用万能头文件)
#include<iostream>
using namespace std;
const int MAXN=110,MAXM=110;
int N,M;
char mp[MAXN][MAXM];
int dx[]={-1,-1,-1,0,0,0,1,1,1};
int dy[]={-1,0,1,-1,0,1,-1,0,1};
void dfs(int x,int y){
mp[x][y]='.';
for(int i=0; i<9; i++){
int nx = x+dx[i], ny = y+dy[i];
if(nx >= 0 && nx < N && ny >= 0 && ny < M && mp[nx][ny]=='W'){
dfs(nx,ny);
}
}
}
int main(){
while(~(scanf("%d%d",&N,&M))){
for(int i=0;i<N;i++){
scanf("%s",mp[i]);
}
int res = 0;
for(int i=0;i<N;i++){
for(int j=0;j<M;j++){
if(mp[i][j]=='W'){
dfs(i,j);
res++;
}
}
}
printf("%d\n", res);
}
}
宽度优先搜索
对初始位置,由近及远地进行搜索。可以求最短路径、最少操作之类的。
用队列实现。
首先讲所有距离初始记为INF(=0x3f3f3f3f),搜索过程不断调整:d[nx][ny]=min(d[nx][ny],d[x][y]+1)
经过搜索后如果还是INF,就是不可达。
例1 迷宫:给定N*M的迷宫,每一步可向相邻四格移动。求起点到终点最小步数。
N,M<=100, ‘#’, ‘.’, ‘S’, 'G’分别表示墙壁、通道、起点、终点。
const int INF = 0x3f3f3f3f, NMAX=110, MMAX=110;
typedef pair<int, int> P;
char mp[NMAX][MMAX];
int N,M;
int sx,sy,gx,gy;
int d[NMAX][MMAX];
int dx[]={-1,0,1,0};
int dy[]={0,-1,0,1};
int bfs(sx,sy,gx,gy){
queue<P> q;
for(int i=0; i<N; i++)
for(int j=0; j<M; j++)
d[i][j]=INF;
q.push(P(sx,sy));
d[sx][sy]=0;
while(!q.empty()){
P now = q.front();
q.pop();
if(p.first==gx && p.second==gy) break;
for(int i=0;i<4;i++){
int nx = p.first+dx[i], ny = p.second+dy[i];
if(nx>=0 && nx<N && ny>=0 && ny<M
&&mp[nx][ny]!='#' && d[nx][ny]==INF){
q.push(P(nx,ny));
d[nx][ny] = d[p.first][p.second]+1;
}
}
}
return d[gx][gy];
}
例2 特殊状态枚举,next_permutation函数
#include <cstdio>
#include <cstring>
using namespace std;
const int NMAX=15;
bool used[NMAX];
int perm[NMAX];
void permutation(int pos, int n){
if(pos==n){
for(int i=0;i<n;i++){
printf("%d ", perm[i]);
}printf("\n");
return;
}
for(int i=0; i<n; i++){
if(!used[i]){
perm[pos]=i;
used[i] = true;
permutation(pos+1,n);
used[i] = false;
}
}
return;
}
int main(){
int p;
while(~scanf("%d",&p)&&p){
memset(used,0,sizeof(used));
permutation(0,p);
}
}
剪枝
如深度优先-例1部分和:如果给定条件是数字都大于0,那么如果当前和超过k,必然不可能使和为k。