搜索与回溯(一本通)基础篇
第一类:就是例题,直接搜索完了打标记退回就行了
组合的输出
第一种方法:字典序,典型的DFS,打标记
#include<iostream>
#include<cstdio>
#include<cstring>
#include<iomanip>
using namespace std;
int m,n;
int a[21]={1},b[21];
void print(){
for(int i=1;i<=n;i++) cout<<" "<<a[i];
cout<<endl;
}
int search(int x){
for(int i=a[x-1];i<=m;i++)
if(!b[i]){
a[x]=i;
b[i]=1;
if(x==n) print();
else search(x+1);
b[i]=0;
}
}
int main(){
cin>>m>>n;
search(1);
return 0;
}
第二种方法:二进制处理,但是好像不是按照字典序
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1010;
const int INF=0x3fffffff;
typedef long long LL;
typedef unsigned long long ull;
int tot;
//但是这样不是按照字典序
void print(int n,int k){
for(int i=0;i<(1<<n);i++){
int num=0,kk=i;
while(kk){
kk=kk&(kk-1);
num++;
}
if(num==k){
for(int j=0;j<n;j++){
if(i&(1<<j)){
cout<<" "<<j+1;
}
}
tot++;
cout<<endl;
}
}
}
int main(){
int n,k;
cin>>n>>k;
print(n,k);cout<<tot<<endl;
return 0;
}
一、自然数的拆分,这里使用的方法与因子分解哪里差不多,但是注意用这个方法的因子分解要求数据很小
int n,a[10001]={1},total; //这里如果没有将数组a全部赋值为1是不行的
void print(int t){
cout<<n<<"=";
for(int i=1;i<=t-1;i++) cout<<a[i]<<"+";
cout<<a[t]<<endl;
total++;
}
int search(int s,int t){
for(int i=a[t-1];i<=s;i++){
if(i<n){ //注意条件
a[t]=i;
s-=i; //保留结果
if(s==0) print(t); //到达终点
else search(s,t+1);
s+=i; //回溯
}
}
}
int main(){
cin>>n;
search(n,1);
return 0;
}
二、 分为互质组(可能要求数据量比较小?)
其实逻辑理清楚了就很好写,而且这个不属于回溯的范围,就是逻辑题目,每一次递归就产生一个新的组,如果这个组是空的,就放一个进去,然后看剩下的所有的有没有和里面的都互质的,有就放,这就是一个组,然后处理剩下的就行了
int num,n,len;
int flag=0;
int vis[11],map[11][11], a[11];
bool check(int a,int b){
for(int i=2;i<=a/2&&i<=b/2;i++){
if(a%i==0&&b%i==0) return false;
}
return 1;
}
void dfs(int k){
num=k-1;//为什么要是k-1因为最后会递归到k+1才会退出
if(len==0) return ;
int i;
if(map[k][0]==0){ //第K组没有数据
for(i=flag+1;i<=n;i++){
if(vis[i]==0){
map[k][1]=a[i];
map[k][0]++;
vis[i]=1;
len--;//减少一个
flag=i;//下次从这里开始
break;
}
}
} //一定要注意flag的运用!!!!避免多次重复计算
//如果这个分组有数据
//注意这里没有else!!!!!
for(i=flag+1;i<=n;i++){
if(vis[i]==0){
bool yes=1;//判断与这个分组里里面的数据是不是互斥
for(int j=1;j<=map[k][0];j++){
if(check(map[k][j],a[i])==0){
yes=0;
break;
}
}
if(yes==1){
map[k][0]++;
map[k][map[k][0]]=a[i];
vis[i]=1;
len--;
}
}
}
dfs(k+1); //进行下一组
return ;
}
int main(){
cin>>n;
len=n;
for(int i=1;i<=n;i++) cin>>a[i];
memset(vis,0,sizeof(vis));
memset(map,0,sizeof(map));
dfs(1);
cout<<num<<endl;
}
四、单词接龙
单词接龙是一个与我们经常玩的成语接龙相类似的游戏,现在我们已知一组单词,且给定一个开头的字母,要求出以这个字母开头的最长的“龙”(每个单词都最多在“龙”中出现两次),在两个单词相连时,其重合部分合为一部分,例如beast和astonish,如果接成一条龙则变为beastonish,另外相邻的两部分不能存在包含关系,例如at和atide间不能相连。
当时做的时候觉得这道题好难噢,注意细节和思路!!!
我草,用这个代码的话根本不能解决不重合的问题,照样是可以的(不懂)
string str[1000];
int n,vis[1001]={0},length=0;
//检查两个字符串的重叠部分
inline int check(string a,string b){
int p=min(a.length(),b.length());
for(int i=1;a.length()==1? i<=p:i<p;i++){ //!!
bool flag=1;//这个位置错了!!
//这个三目运算符看懂,要特判字符串是不是只有一个单词,是的话下面循环会直接退出
//如果不是为1那么为i<p因为从0开始
for(int j=0;j<i;j++){
if(a[a.length()-i+j]!=b[j]){//如果不匹配,注意这个是a.length()-i+j
flag=0;
break;
}
}
if(flag==1) return i; //相同部分的长度
}
return 0;
}
//接下来是搜索过程
void dfs(string s,int length_now){
length=max(length,length_now);//每次搜索都要特判
for(int i=1;i<=n;i++){ //对每个字符串都要判断(暴力搜索)
if(vis[i]>1) continue;//如果使用了两次就退出
else{
int add=check(s,str[i]);//不用纠结上面判断a.length()==0,因为这是个循环,每个字符串都会被判断
if(add!=0){
vis[i]++;
dfs(str[i],length_now+str[i].length()-add);//新的长度为现在长度加字符串长度-重叠长度
vis[i]--;//回溯
}
}
}
}
int main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>str[i];
cin>>str[n+1];
dfs(str[n+1],1);
cout<<length<<endl;
return 0;
}
1212:LETTERS

换了之后搜索,然后马上换回来,one of例题
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1010;
const int INF=0x3fffffff;
typedef long long LL;
typedef unsigned long long ull;
int r,s,ans;
char mp[30][30];
int vis[30][30];
int dis[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
void change(char x){
for(int i=1;i<=r;i++){
for(int j=1;j<=s;j++){
if(mp[i][j]==x) vis[i][j]=!vis[i][j];
}
}
}
void dfs(int x,int y,int tot){
ans=max(ans,tot);
for(int i=0;i<4;i++){
int xx=x+dis[i][0];
int yy=y+dis[i][1];
if(xx>=1&&xx<=r&&yy>=1&&yy<=s&&!vis[xx][yy]){
change(mp[xx][yy]);
dfs(xx,yy,tot+1);
change(mp[xx][yy]);
}
}
}
int main(){
cin>>r>>s;
for(int i=1;i<=r;i++){
scanf("%s",mp[i]+1);
}
ans=1;
change(mp[1][1]);
dfs(1,1,1);
cout<<ans<<endl;
return 0;
}
1218:取石子游戏
假设石子数目为(a,b)且a >= b,如果[a/b] >= 2则先手必胜,如果[a/b]<2,那么先手只有唯一的一种取法。[a/b]表示a除以b取整后的值。
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
using namespace std;
int a,b;
bool get(int x,int y){
if(x<y) swap(x,y);
if(x/y>=2||x==y) return 1;
else return !get(y,x-y);//这是辗转相除法的思想 ,注意取反
}
int main(){
/*while(cin>>a>>b){
if(a==0&&b==0) break;
if(a<b) swap(a,b);
if(a/b<2){
for(int i=1;a>=0&&b>=0;i++){
if(a<b) swap(a,b);
int k;
for(k=1;a-k*b>=0;k++);
a-=(k-1)*b;
if(a==0||b==0){
if(i%2==1) cout<<"win"<<endl;
else cout<<"lose"<<endl;
break;
}
}
}
else {
cout<<"win"<<endl;
}
}
*/
//我不太理解这道题,明明就可以取更多的,为什么不取更多,最优取法是什么?
//本题的解法挺简单,但是我还是不不知道为什么我上面的写法是错误的;
while(cin>>a>>b){
if(a==0&&b==0) break;
if(get(a,b)) cout<<"win"<<endl;
else cout<<"lose"<<endl;
}
return 0;
}
1222:放苹果
要么全不放,要么都放一个再说,注意是没苹果了或者只有一个盘子了才返回1
#include<iostream>
#include<cstdio>
#include<iomanip>
#include<cstring>
#include<cstdlib>
using namespace std;
int find(int m,int n){
if(m==0||n==1) return 1;
else if(n>m) return find(m,m);
else return find(m,n-1)+find(m-n,n);
}
int main(){
int sum;cin>>sum;
int x,y;
while(sum--){
cin>>x>>y;
cout<<find(x,y)<<endl;
}
return 0;
}
1213:八皇后问题
例题,写一次吧,三个数组用来判断
a[]是用来记录结果的,b[]是用来表示这个皇后有没取过,c[]是正对角线,d[]是斜对角线
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1010;
const int INF=0x3fffffff;
typedef long long LL;
typedef unsigned long long ull;
int a[10],b[20],c[20],d[20];
int ans;
void print(){
cout<<"No. "<<++ans<<endl;
for(int i=1;i<=8;i++){
for(int j=1;j<=8;j++){
if(i==a[j]) cout<<"1 ";
else cout<<"0 ";
}
cout<<endl;
}
}
//a[]是用来记录结果的,b[]是用来表示这个皇后有没取过,c[]是正对角线,d[]是斜对角线
void dfs(int x){
for(int i=1;i<=8;i++){
if(!b[i]&&!c[x+i]&&!d[x-i+7]){ //一个是列,
b[i]=1;
c[x+i]=1;
d[x-i+7]=1;
a[x]=i;
if(x==8) print();
else dfs(x+1);
b[i]=0;
c[x+i]=0;
d[x-i+7]=0;
}
}
}
int main(){
dfs(1);
return 0;
}
1216:红与黑
这个题目就没有回溯了,因为是求的最大值,而不是所有的值,全都试一遍总能找到
#include<iostream>
#include<cstring>
using namespace std;
char a[21][21];
int aa[21][21],pd[21][21],m,n,sum;
int l[4][2]={{0,-1},{0,1},{1,0},{-1,0}};
int search(int x,int y){
int nx,ny;
for(int i=0;i<4;i++){
nx=x+l[i][0];ny=y+l[i][1];
if(nx>=0&&nx<n&&ny>=0&&ny<m&&aa[nx][ny]&&(!pd[nx][ny])){
pd[nx][ny]=1;
sum++;
search(nx,ny);
}
}
}
int main(){
for(;;){
memset(aa,0,sizeof(aa));
memset(pd,0,sizeof(pd));
int x,y;sum=1;
cin>>m>>n;
if(m==0&&n==0) break;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++) {
cin>>a[i][j];
if(a[i][j]=='@') {x=i;y=j;aa[x][y]=1;
}
if(a[i][j]=='.') aa[i][j]=1;
if(a[i][j]=='#') aa[i][j]=0;
}
}
pd[x][y]=1;
search(x,y);
cout<<sum<<endl;
}
return 0;
}
1215:迷宫
这也是不需要回溯的,因为只需要判断能不能走得到,而不涉及方案的
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
using namespace std;
int k,n;
int dis[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
int vis[101][101];
char map[101][101];
int sx,sy,ex,ey;
bool ok=0;
void find_way(int x,int y){
vis[x][y]=1;
for(int i=0;i<4;i++){
int xx=x+dis[i][0];
int yy=y+dis[i][1];
if(xx>=0&&xx<n&&yy>=0&&yy<n&&!vis[xx][yy]){
vis[xx][yy]=1;
if(xx==ex&&yy==ey){
ok=1;
cout<<"YES"<<endl;
return;
}
else find_way(xx,yy);
}
}
}
int main(){
cin>>k;
char x;
while(k--){
memset(vis,0,sizeof(vis));
memset(map,0,sizeof(map));
cin>>n;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++) {
cin>>map[i][j];
if(map[i][j]=='#') vis[i][j]=1;
}
}
ok=0;
cin>>sx>>sy>>ex>>ey;
if(map[sx][sy]=='#'||map[ex][ey]=='#') {
cout<<"NO"<<endl;
continue;
}
find_way(sx,sy);
if(!ok) cout<<"NO"<<endl;
}
return 0;
}
1217:棋盘问题
1219:马走日(做法一样的)
这里求的是方案数量,所以要回溯,需要注意的是通过下标直接控制了行
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
using namespace std;
char map[101][101];
int num,k,n;
int vis[101];
void dfs(int x,int step){
if(step==k) {num++;return;}
for(int i=x;i<=n;i++){ //这里的技巧!!!!i=x就直接避免了x相同的可能性
for(int j=1;j<=n;j++){
if(map[i][j]=='#'&&!vis[j]) {
vis[j]=1;
dfs(i+1,step+1);//这里的i是+1!!!!!不然就是可能在同一行!!!!不需要在参数中加入列,直接枚举即可
vis[j]=0; //在这里面回溯
}
}
}
}
int main(){
char x;
while(cin>>n>>k){
num=0;
memset(vis,0,sizeof(vis));
if(n==-1&&k==-1) break;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
cin>>map[i][j];
}
dfs(1,0);
//step的初值是0!!!!
cout<<num<<endl;
}
return 0;
}
广度优先搜索(一本通基础篇)
首先是例题:
1329:【例8.2】细胞
在函数里面定义的队列,因为是求的个数,所以函数用一次就是一个细胞ps.节点队列学学
1249:Lake Counting(和这个做法一模一样)
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
int n,m,sum;
char a[301][301];
int dir[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
struct node{
int x,y;
};
void bfs(int x,int y){
queue<node> q;
a[x][y]='0';
node c;c.x=x;c.y=y;
q.push(c);
while(!q.empty()){
node d=q.front();
for(int i=0;i<4;i++){
int xx=d.x+dir[i][0];
int yy=d.y+dir[i][1];
if(xx>=1&&xx<=n&&yy>=1&&yy<=m&&a[xx][yy]!='0'){
a[xx][yy]='0';
node newo;
newo.x=xx;
newo.y=yy;
q.push(newo);
}
}
q.pop();
}
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++) cin>>a[i][j];
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
if(a[i][j]!='0'){
sum++;
bfs(i,j);
}
}
cout<<sum<<endl;
return 0;
}
1330:【例8.3】最少步数
1251:仙岛求药(和这个做法一模一样)
也很简单,就是节点多了一个参数:步数ps.节点初始化函数书写
#include<bits/stdc++.h>
using namespace std;
struct node
{
int x,y,step;
node(){}
node(int x1,int y1,int step1):x(x1),y(y1),step(step1){}
};
const int N=150;
int u[12][2]={{1,2},{1,-2},{-1,2},{-1,-2},{-2,-1},{-2,1},{2,1},{2,-1},{2,2},{-2,-2},{2,-2},{-2,2}};
int vis[N][N];
int a,b,c,d;
void bfs(int x,int y)
{
memset(vis,0,sizeof(vis));
vis[x][y]=1;
queue<node>Q;
Q.push(node(x,y,0));
while(!Q.empty()){
node a=Q.front();
Q.pop();
for(int i=0;i<12;i++){
int xx=a.x+u[i][0];
int yy=a.y+u[i][1];
if(xx>=1&&xx<=100&&yy>=1&&yy<=100&&(vis[xx][yy]==0)){
vis[xx][yy]=1;
Q.push(node(xx,yy,a.step+1));
if(xx==1&&yy==1){
cout<<a.step+1<<endl;
return;
}
}
}
}
}
int main()
{
cin>>a>>b>>c>>d;
bfs(a,b);
bfs(c,d);
return 0;
}
1248:Dungeon Master
三维的迷宫,不难,和二维的一样,注意细节就好
int summ;
int l,n,m;
bool ok;
int dir[6][3]={{1,0,0},{-1,0,0},{0,1,0,},{0,-1,0},{0,0,-1},{0,0,1}};
struct node{
int l,x,y;
int step;
};
node start,end;
char map[50][50][50];
bool vis[50][50][50];
void bfs(int ll,int xx,int yy,int step){
queue<node> p;
node d;
d.l=ll;d.x=xx;d.y=yy;d.step=step;
map[ll][xx][yy]='#';
p.push(d);
vis[ll][xx][yy]=1;
while(!p.empty()){
node s=p.front();
for(int i=0;i<6;i++){
int lll=s.l+dir[i][0];int xxx=s.x+dir[i][1];int yyy=s.y+dir[i][2];
if(lll>-1&&lll<l&&xxx>-1&&xxx<n&&yyy>-1&&yyy<m&&map[lll][xxx][yyy]=='.'&&vis[lll][xxx][yyy]==0){
if(lll==end.l&&xxx==end.x&&yyy==end.y) {
summ=s.step+1;
ok=1;
return;
}
node ss;
map[lll][xxx][yyy]='#';
vis[lll][xxx][yyy]=1;
ss.l=lll;ss.x=xxx;ss.y=yyy;ss.step=s.step+1;
p.push(ss);
}
}
p.pop();
}
}
也是三维搜索:
hud 1240 Asteroids!.cpp
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1010;
const int INF=0x3fffffff;
typedef long long LL;
//三维BFS
struct node{
int x,y,z;
int num;
};
int dis[6][3]={{0,1,0},{0,-1,0},{1,0,0},{-1,0,0},{0,0,1},{0,0,-1}};
int n;
int sx,sy,sz,ex,ey,ez;
int mp[12][12][12];
int step[12][12][12];
int tot=0;
void bfs(int z,int x,int y){
node a;
a.x=sx;a.y=sy;a.z=sz;
a.num=0;
step[sz][sx][sy]=0;
queue<node> q;
q.push(a);
while(!q.empty()){
node u=q.front();
q.pop();
//先判断
if(u.x==ex&&u.z==ez&&u.y==ey){
tot=u.num;
return;
}
for(int i=0;i<6;i++){
int xx=u.x+dis[i][0];
int yy=u.y+dis[i][1];
int zz=u.z+dis[i][2];
if(xx>=0&&xx<n&&yy>=0&&yy<n&&zz>=0&&zz<n&&mp[zz][xx][yy]!=INF){
if(step[zz][xx][yy]>step[u.z][u.x][u.y]+1){
step[zz][xx][yy]=step[u.z][u.x][u.y]+1;
mp[zz][xx][yy]=INF;
node next;
next.x=xx;next.y=yy;next.z=zz;
next.num=u.num+1;
q.push(next);
}
}
}
}
}
int main(){
char st[21],ed[21],op;
while(scanf("%s %d",st,&n)!=EOF){
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
for(int z=0;z<n;z++){
cin>>op;
if(op=='X') mp[i][j][z]=INF;
if(op=='0') mp[i][j][z]=1;
step[i][j][z]=INF;
}
}
}
cin>>sx>>sy>>sz>>ex>>ey>>ez;
cin>>ed;
tot=0;
bfs(sz,sx,sy);
if(step[ez][ex][ey]!=INF){
cout<<n<<" "<<tot<<endl;
}
else cout<<"NO ROUTE"<<endl;
}
return 0;
}
1250:The Castle
这道题就特殊在输入了,不过也没什么,判断能不能联通一个&就可以了
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<string>
#include<cstdlib>
#include<queue>
#include<vector>
#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define N 51
#define MOD 2520
#define E 1e-12
using namespace std;
int n,m;
int a[N][N];
int b[4]={1,2,4,8};
bool vis[N][N];
int sum,maxx;
int dir[4][2]={{0,-1},{-1,0},{0,1},{1,0}};
struct node
{
int x;
int y;
}q[N*100];
void bfs(int x0,int y0)
{
int head=1,tail=1;
int cnt=1;
vis[x0][y0]=1;
q[tail].x=x0;
q[tail].y=y0;
tail++;
while(head<tail)
{
int x=q[head].x;
int y=q[head].y;
for(int i=0;i<4;i++)
{
int nx=x+dir[i][0];
int ny=y+dir[i][1];
if(nx>=1&&nx<=n&&ny>=1&&ny<=m&&vis[nx][ny]==0&&(a[x][y]&b[i])==0)
{
cnt++;
vis[nx][ny]=1;
q[tail].x=nx;
q[tail].y=ny;
tail++;
}
}
head++;
}
if(cnt>maxx)
maxx=cnt;
sum++;
}
int main()
{
memset(vis,0,sizeof(vis));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&a[i][j]);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(vis[i][j]==0)
bfs(i,j);
printf("%d\n%d\n",sum,maxx);
return 0;
}
六、抓住那头牛
农夫知道一头牛的位置,想要抓住它。农夫和牛都位于数轴上,农夫起始位于点N(0≤N≤100000),牛位于点K(0≤K≤100000)。农夫有两种移动方式:
1、从X移动到X-1或X+1,每次移动花费一分钟
2、从X移动到2*X,每次移动花费一分钟
假设牛没有意识到农夫的行动,站在原地不动。农夫最少要花多少时间才能抓住牛?
int num,head,tail;
int n,m;
int vis[N];
int dir[2]={1,-1};
int f[N*2][2];
void find(int x,int y){
head=1;tail=1;
f[1][0]=x;f[1][1]=0;
vis[x]=1;
tail++;
while(head<tail){
int x0=f[head][0];
if(x0==y) {
cout<<f[head][1]<<endl;
return ;
}
int xx;
for(int i=0;i<2;i++){
xx=x0+dir[i];
if(xx>=0&&xx<1000001&&vis[xx]==0){
f[tail][0]=xx;
f[tail][1]=f[head][1]+1;
vis[xx]=1;
tail++;
}
}
xx=x0*2;
if(xx>=0&&xx<100001&&vis[xx]==0){
f[tail][0]=xx;
f[tail][1]=f[head][1]+1;
vis[xx]=1;
tail++;
}
head++;
}
}
int main(){
cin>>n>>m;
if(n>m) cout<<n-m<<endl;
else {
find(n,m);
}
return 0;
}
posted on
浙公网安备 33010602011771号