白书(二)-2.1穷尽搜索(练习题)(自我总结)

做这一章时候,主要的漏洞是:

1.没有写搜素结束的条件。

2.对于如何设置搜素状态不清晰。 

3.题目的意思没有解读清楚就做题

z​​​​​A - Red and Black

#include<iostream>
#include<cstring> 
using namespace std;
int w,h;
char mp[21][21];
bool vis[21][21];
int startx,starty;
int dx[4]={0,0,1,-1};
int dy[4]={1-1,0,0};
int length;
void dfs(int x,int y)
{

if(x<1||x>h||y<1||y>w) return ; 
	if(mp[x][y]=='#') return ;        
	mp[x][y]='#';
	length++;          
	dfs(x-1,y);     
	dfs(x+1,y);
	dfs(x,y+1);
	dfs(x,y-1);


	
}



int main()
{
	
	while(cin>>w>>h&&w!=0&&h!=0)
	{
		length=0;
	memset(vis,false,sizeof vis);
	for(int i=1;i<=h;i++)
	{
		for(int j=1;j<=w;j++)
		{
			cin>>mp[i][j];
			if(mp[i][j]=='@')
			{
				startx=i;
				starty=j;

			}
		}
		

	
	 }
	
		dfs(startx,starty);
		cout<<length<<endl;	
	 } 
	
	
	
	
	
	return 0;
 } 

B - Property Distribution

#include<iostream>
#include<cstring> 
using namespace std;
int w,h;
char mp[21][21];
bool vis[21][21];
int startx,starty;
int dx[4]={0,0,1,-1};
int dy[4]={1-1,0,0};
int length;
void dfs(int x,int y)
{

if(x<1||x>h||y<1||y>w) return ; 
	if(mp[x][y]=='#') return ;        
	mp[x][y]='#';
	length++;          
	dfs(x-1,y);     
	dfs(x+1,y);
	dfs(x,y+1);
	dfs(x,y-1);

}

int main()
{
	
	while(cin>>w>>h&&w!=0&&h!=0)
	{
		length=0;
	memset(vis,false,sizeof vis);
	for(int i=1;i<=h;i++)
	{
		for(int j=1;j<=w;j++)
		{
			cin>>mp[i][j];
			if(mp[i][j]=='@')
			{
				startx=i;
				starty=j;

			}
		}
		

	
	 }
	
		dfs(startx,starty);
		cout<<length<<endl;	
	 } 
	
	
	
	
	
	return 0;
 } 

  

C - Ball

  从这题开始受到了点启发,往左又可以分为往右,往左,层层深入,值得思考的就是用l和r记录了往左和往右的当前最值,当总共编号能到达10时,说明1-9全进去了,则结束循环。

#include<bits/stdc++.h>

using namespace std;

int h,w;
const int N=101;

bool vis[16];
int a[11],l=0,r=0,flag,n;

	//i为第几个数的编号 
	//num记录当前已经枚举的数目。 
int dfs(int i)
{
	 int iv;
	 if(i==10)
	 {
	  flag=1;
	  return(flag);
	 }
	 
	 if(a[i]>l)
	 {
	 	iv=l;
	 	l=a[i];
	 	dfs(i+1);
	 	l=iv;
	 }
	  if(a[i]>r)
	 {
	 	iv=r;
	 	r=a[i];
	 	dfs(i+1);
	 	r=iv;
	 }
}


int main()
{
	int t;
 	cin>>t;
 	while(t--)
	{
		flag=0;
 		for(int i=0;i<10;i++) cin>>a[i];
 		dfs(1);
 		if(flag)
  			cout<<"YES"<<endl;
  		else
   			cout<<"NO"<<endl;
 		
	}
   	

   	

    return 0;
}

D - Curling 2.0

 主要这题给人启发的就是对于持续前进是如何处理的,以及对于回溯的应用还有一些其他小细节

#include<iostream>

using namespace std;
int mp[21][21];
int m,n,ex,ey,sx,sy;
int next[4][2]={0,1, 1,0, 0,-1, -1,0};
int beginx,beginy;
int finalx,finaly;
int minx;
void dfs(int x,int y,int step)
{
	step++;
	int dx,dy;
	if(step>10) return ;
	for(int i=0;i<4;i++)
	{
		dx=x+next[i][0];
		dy=y+next[i][1];
		if(mp[dx][dy]==1) continue;
		

		while(mp[dx][dy]==0||mp[dx][dy]==2)
		{
			dx+=next[i][0];
			dy+=next[i][1];
		}
		if(dx<0||dx>=n||dy<0||dy>=m) continue;
		if(mp[dx][dy]==1)
		{
			mp[dx][dy]=0;
			dfs(dx-next[i][0],dy-next[i][1],step);
			mp[dx][dy]=1;//回溯 
		}
		
		if(mp[dx][dy]==3&&step<minx)
		{
			minx=step;	
			continue; 
		 } 
		
		
	}


}



int main()
{
    int i,j;
    while(scanf("%d%d",&m,&n)!=EOF)
    {
        if(m==0&&n==0)
            break;
        for(i=0;i<n;i++)
            for(j=0;j<m;j++)
            {
                scanf("%d",&mp[i][j]);
                if(mp[i][j]==2)
                {
                    sx=i;
                    sy=j;
                }
                if(mp[i][j]==3)
                {
                    ex=i;
                    ey=j;
                }
            }
        minx=1010;
        dfs(sx,sy,0);
        if(minx>10)
            printf("-1\n");
        else
            printf("%d\n",minx);
    }
    return 0;
 }

E - Cheese

 启发我bfs不是说就是仅仅只是起点和终点,也可以是一个1起点到1个终点,再以这个终点为起点到其他终点。

#include<queue>
#include<iostream>
using namespace std;
#include<algorithm> 
const int INF=1e9;
const int MAX=1000+10;
int dx[]={1,0,-1,0},dy[]={0,1,0,-1};
//也可以考虑用二维方向数组解决 
typedef pair<int,int> P; 
char maze[MAX][MAX];
int N,M;
int sx,sy;//起点
int gx,gy;//终点
int d[MAX][MAX];
void init(){
	for(int i=0;i<N;i++)
		for(int j=0;j<M;j++)
			d[i][j]=INF;
}
int bfs(){
	queue<P>que;
	que.push(P(sx,sy));
	d[sx][sy]=0;
	while(que.size())
	{
		P p=que.front();
		que.pop();
		if(p.first==gx&&p.second==gy) break;
		for(int i=0;i<4;i++)
		{
			int nx=p.first+dx[i];
			int ny=p.second+dy[i];
			if(nx>=0&&nx<N&&ny>=0&&ny<=M&&maze[nx][ny]!='X'&&d[nx][ny]==INF)
			{
				que.push(P(nx,ny));
				d[nx][ny]=d[p.first][p.second]+1;
			}
		}
	 } 




	return d[gx][gy];
} 



int main(){
int nn;//从1-2-3----nn依次计算; 
	cin>>N>>M>>nn; 
	for(int i=0;i<N;i++)
			scanf("%s",&maze[i]);


	//找起点
for(int i=0;i<N;i++)
		for(int j=0;j<M;j++)
			if(maze[i][j]=='S'){
				sx=i,sy=j;
			}
			else if(maze[i][j]>='1'&&maze[i][j]<='9')
				maze[i][j]=maze[i][j]-'0';
	int ans=0,cnt=1,flag=0;

	for(int i=0;i<N;i++){ 
		for(int j=0;j<M;j++){ 
			if(maze[i][j]==cnt){
				init();
				gx=i,gy=j;
				ans+=bfs();
				sx=gx,sy=gy;
			
				cnt++;
				i=0,j=0; 
				if(cnt==nn+1){
					flag=1;
					break;
				}
			} 
		}
		if(flag)
			break;
	}
	cout<<ans<<endl;		

	return 0;	
}

F - Meteor Shower

1. 关键在于对每个点将被摧毁时间的处理,即每个点标记的是最早被摧毁的时间。

2.当该点时间+1小于下一个移动到的点的时候就可以移动到下一个点。

3.记得初始化d[0][0]=0

4.特殊情况(0,0)一开始被轰炸也得考虑

#include <cstdio>
#include <iostream>
#include <queue>
#include <algorithm>

using namespace std;

typedef pair<int, int> P;

const int MAX_M = 50000;
const int MAX_N = 400 + 1;
const int INF = 100000000;

//输入
int M;
int X[MAX_M], Y[MAX_M], T[MAX_M];

int maze[MAX_N][MAX_N];                        //保存地图
int d[MAX_N][MAX_N];                        //保存最短步数

//4个方向
const int dx[4] = {-1, 1, 0, 0};
const int dy[4] = {0, 0, -1, 1};
 int bfs()
{
	if(maze[0][0]==0) return -1;
	queue<P> que;
	que.push(P(0,0));
	d[0][0]=0;//这里没标 
	while(!que.empty())
	{
		P p=que.front();
		que.pop();
		int x=p.first,y=p.second;
		if(maze[x][y]==INF) return d[x][y];
		for(int i=0;i<4;i++)
		{
			int xx=x+dx[i],yy=y+dy[i];
			if(xx>=0&&yy>=0&&d[xx][yy]==INF&&d[x][y]+1<maze[xx][yy])
			{
				que.push(P(xx,yy));
				d[xx][yy]=d[x][y]+1;
			}
		}
		
	}
	

	return -1;
	
}
void solve(){
    //初始化地图
    for(int i = 0; i < MAX_N; i ++)
        fill(maze[i], maze[i] + MAX_N, INF);
    //模拟轰炸场景
    for(int i = 0; i < M; i ++){
        maze[X[i]][Y[i]] = min(maze[X[i]][Y[i]], T[i]);
        for(int j = 0; j < 4; j ++){
            int nx = X[i] + dx[j], ny = Y[i] + dy[j];
            if(0 <= nx && 0 <= ny)
                maze[nx][ny] = min(maze[nx][ny], T[i]);
        }
    }
    //初始化地图最小步数
    for(int i = 0; i < MAX_N; i ++)
        fill(d[i], d[i] + MAX_N, INF);
    //宽度优先搜索
    int ans = bfs();
    printf("%d\n", ans);
}
int main(){

    scanf("%d", &M);
    for(int i = 0; i < M; i ++){
        scanf("%d %d %d", &X[i], &Y[i], &T[i]);
    }
    solve();
    return 0;
}

G - Seven Puzzle

 1.新的转态记录:用map类型记录了string类型的状态以及其与初始转态的距离。

2.find()函数的应用值得学习。

3.s.erase(remove(s.begin(),s.end(),' '),s.end());的搭配应用

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
const double eps = 1e-8;
const int NINF = 0xc0c0c0c0;
const int INF  = 0x3f3f3f3f;
const ll  mod  = 1e9 + 7;
const ll  maxn = 1e6 + 5;
const int N = 10;

int a[N];
string s="01234567";
map<string,int>mp;
int dx[4]={1,-1,4,-4};



void bfs()
{ 
	queue<string>q;
	q.push(s);
	while(!q.empty())
	{
		string p=q.front();
		q.pop();
		int pos=p.find('0');
		for(int i=0;i<4;i++)
		{
			int tp=pos+dx[i];
			if(tp>=0&&tp<=7&&(!((pos==3)&&i==0)) && (!((pos==4)&&i==1)))
			{
				string k=p;
				swap(k[pos],k[tp]);
			if(mp[k]==0 && k!="01234567"){
					mp[k]=mp[p]+1;
					q.push(k);
				}
			}
		}
	}
		

	
}





int main(){
	bfs();
	while(getline(cin,s)){
		s.erase(remove(s.begin(),s.end(),' '),s.end());
		printf("%d\n",mp[s]);
	}
	return 0;
}

H - Smallest Difference

 贪心(前n/2位与后n-n/2位比较,使得两数位数相差最小)加上对stl函数 next_permutation的应用。但是注意对前导0的处理。

#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#define INF 0x3f3f3f3f
using namespace std;

int num[11];
int n;
string line;

int main()
{
	
	cin>>n;
	getchar(); 
	while(n--)
	{
		getline(cin,line);
		int i=0;
		int ans=INF;
	
		for(int j=0;j<line.length();j++)
		{
		if(line[j]!=' ') 
			num[i++]=(line[j]-'0');
		}
		
	
		if(i==2) 
		{
		cout<<num[1]-num[0]<<endl;
		continue;
		}
		do{
			if(num[0]==0||num[i/2]==0) continue;
			int a=0,b=0;
			for(int j=0;j<i/2;j++) a=a*10+num[j];
			for(int j=i/2;j<i;j++) b=b*10+num[j];
			ans=min(ans,abs(b-a));
		}while(next_permutation(num,num+i));
		
		cout<<ans<<endl;
		
	}
	
	return 0;
}

I - Backward Digit Sums

 也是对状态的枚举,主要是judge函数对类似于杨辉三角的处理。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<set> 
#include<vector>
 

using namespace std;


int a[11],n,sum,b[11];

bool judge(){
    int tmp;
    for(int i=0;i<n-1;i++){
    	b[i]=a[i]+a[i+1];
	}
    for(int i=n-2;i>0;i--){
    	for(int j=0;j<i;j++){
    		b[j]+=b[j+1];
		}
	}
	if(b[0]==sum)return true;
	else return false;

}


int main()
{
scanf("%d%d",&n,&sum);
	for(int i=0;i<n;i++){
		a[i]=i+1;
	}
	if(n==1){
		printf("1\n");
		return 0;
	}

	do
	{
	
		if(judge()){
		
			for(int i=0;i<n-1;i++)
		
			{
			
				printf("%d ",a[i]);
			}
		
			printf("%d\n",a[n-1]);
			return 0;
		
	}
	}while(next_permutation(a,a+n));


	return 0;
}

J - Hopscotch

 暴搜,对所有转态记录去重运用了用数组储存位,然后再把值算出来放入set中。

#include<iostream>
#include<cstring>
#include<set>
using namespace std;

int maze[6][6];
int num[6];
set<int >s;

void dfs(int r,int c,int sum)
{
	if(r<1||c<1||r>5 ||c>5) return ;
	
	
	if(sum==7)
	{
		int a=0;
		for(int i=1;i<=sum;i++) a+=a*10+num[i];
		s.insert(a);
		return ;
	 } 
	
	num[sum]=maze[r][c];
	dfs(r+1,c,sum+1);
	dfs(r-1,c,sum+1);
	dfs(r,c+1,sum+1);
	dfs(r,c-1,sum+1);
}


int main()
{
	memset(maze,-1,sizeof maze);
	for(int i=1;i<=5;i++)
	{
		for(int j=1;j<=5;j++)
		{
			cin>>maze[i][j]; 
		}
	}
	
		for(int i=1;i<=5;i++)
	{
		for(int j=1;j<=5;j++)
		{
			dfs(i,j,1);
		}
	}
	
	
	
	cout<<s.size();	
	
	return 0;
 } 

K - Osenbei

 关键思路在于是否想出列不需要模拟,只要对行翻转与否进行深度搜索,其中每一种转态再对每一列进行判断,由于我们需要得到最多的1,那么对于每一列,如果0多,那么就把每一列0个数记为1的个数即可实现翻转,这样处理,是可以穷尽每一种状态的最大值的。

#include<iostream>
#include<cstring>
#include<set>
using namespace std;

int maze[15][10010];
int num[6];
int m,n; 
int maxx;
void dfs(int sum)
{
	if(sum==m)
	{
		int sum1=0;
		for(int i=0;i<n;i++)
		{
			int  sum2=0;//模拟列数操作
			for(int j=0;j<m;j++)
			{
				if(maze[j][i]==1) sum2+=1;
			 } 
			sum1+=max(sum2,m-sum2);//翻转或不翻转 
		
		}
			maxx=max(maxx,sum1); 
		
		return ;//别忘了结束 
	} 
	
	dfs(sum+1);//行不翻转
	for(int i=0;i<n;i++)
	maze[sum][i]=! maze[sum][i];
	dfs(sum+1);//行翻转
	
	
	
}


int main()
{
	while(cin>>m>>n&&m&&n)
	{
		maxx=0;
		for(int i=0;i<m;i++)
		{
			for(int j=0;j<n;j++)
			{
				cin>>maze[i][j];
			}
		 } 
			dfs(0);//对行进行枚举。 
		cout<<maxx<<endl;
	}
	
	

	
	return 0;
 } 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值