Deep First Search && Breadth First Search
T1:Lg P1123 取数游戏
难度 ★
从(1,1)点出发,向右扩展,直到扩展到最后(n,m)
几个问题
1.如何保证搜索顺序?(x,y)→(x,y+1)当y==m时换行
2.如何换行?x=x+1,y=1;
3.如何设计状态?dfs(x,y,sum)表示当前点(x,y)的ans为sum
4.如何保证相邻8个格子只有一个被选上?①【如果当前(x,y)周围都没有被选上的点,那么就可以选,】②【继续dfs下一个点,sum要改变。】③【如果当前(x,y)周围有被选上的点,那么就直接dfs下一个点,sum不会改变】
至此,算法的大致框架已经出来了。CODE如下
#include <bits/stdc++.h>
using namespace std;
const int maxn=2000;
int T,n,m;
int used[maxn][maxn],a[maxn][maxn];
int ans;
void init()
{
freopen("Lg P1123.in","r",stdin);
}
void dfs(int x,int y,int sum)//对应问题3
{
if(x>n) //搜索到最后一个点
{
ans=max(sum,ans);
return;
}
int nx=x,ny=y+1;//对应问题1
if(ny>m)//对应问题2
{
nx=x+1;
ny=1;
}
if(!used[x-1][y-1] && !used[x-1][y] && !used[x-1][y+1] && !used[x][y-1] && !used[x][y+1] && !used[x+1][y-1] && !used[x+1][y] && !used[x+1][y+1])
{
used[x][y]=1;//对应问题4①
dfs(nx,ny,sum+a[x][y]);//对应问题4②
used[x][y]=0;//要回溯
}
dfs(nx,ny,sum);//对应问题4③
}
void initdata()
{
memset(used,0,sizeof(used));
memset(a,0,sizeof(used));ans=0;
}
void work()
{
memset(used,0,sizeof(used));
dfs(1,0,0);//如果dfs(1,1,0)那么第一个搜的点就不是(1,1)而是(1,2)
printf("%d\n",ans);
}
void readdata()
{
scanf("%d",&T);
while(T--)
{
initdata();
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&a[i][j]);
work();
}
}
int main()
{
// init();
readdata();
return 0;
}
T2:Lg P1135 奇怪的电梯
难度:★★
一道宽搜练手题
1、对于初始状态入队,设置初始状态为已访问
2、如果队列不为空时,出队队头元素,否则跳到第5步
3、检查出队的元素是否为最终解,如果是则跳到第5步。
4、对于出队的元素,检查所有相邻状态,如果有效并且未访问,则将
所有有效的相邻状态进行入队,并且设置这些状态为已访问,然后
跳到第2步重复执行
5、检查最后出队的元素是否为最终解,如果是输出结果,否则说明无解
——via here
#include <bits/stdc++.h>
using namespace std;
const int maxn=202;
struct edge
{
int floor;
int times;
};
int mapp[maxn],used[maxn];
int n,a,b;
queue<edge>q;
void init()
{
freopen("Lg 1135.in","r",stdin);
}
void readdata()
{
scanf("%d%d%d",&n,&a,&b);
for(int i=1;i<=n;i++)scanf("%d",&mapp[i]);
}
void BFS()
{
edge u,v;
u.floor=a;u.times=0;used[u.floor]=1;
q.push(u);
while(!q.empty())
{
v=q.front();q.pop();
if(v.floor==b)break;
int upnxtfloor=v.floor+mapp[v.floor];//往上走
if(upnxtfloor<=n && !used[upnxtfloor])
{
used[upnxtfloor]=1;
edge e;e.floor=upnxtfloor;e.times=v.times+1;
q.push(e);
}
int downnxtfloor=v.floor-mapp[v.floor];//往下走
if(downnxtfloor>0 && !used[downnxtfloor])
{
used[downnxtfloor]=1;
edge e;e.floor=downnxtfloor;e.times=v.times+1;
q.push(e);
}
}
if(v.floor==b)printf("%d",v.times);
else printf("-1");
}
int main()
{
init();
readdata();
BFS();
return 0;
}
T3:MZOJ1228,驾车旅游
难度:★★★
一道很好的连DFS基本功的题
【问题描述】
如今许多普通百姓家有了私家车,一些人喜爱自己驾车从一个城市到另一个城市旅游。自己驾车旅游时总会碰到加油和吃饭的问题,在出发之前,驾车人总要想方设法得到从一个城市到另一个城市路线上的加油站的列表,列表中包括了所有加油站的位置及其每升的油价(如3.25元/L)。驾车者一般都有以下的习惯:
(1)除非汽车无法用油箱里的汽油达到下一个加油站或目的地,在油箱里还有不少于最大容量一半的汽油时,驾驶员从不在加油站停下来;
(2)在每一个停下的加油站总是将油箱加满;
(3)在加油站加油的同时,买快餐等吃的东西花去20元。
(4)从起始城市出发时油箱总是满的。
(5)加油站付钱总是精确到0.1元(四舍五入)。
(6)驾车者都知道自己的汽车每升汽油能够行驶的里程数。
现在要你帮忙做的就是编写一个程序,计算出驾车从一个城市到另一个城市的旅游在加油和吃饭方面最少的费用。
【输入】
第一行是一个实数,是从出发地到目的地的距离(单位:km)。
第二行是三个实数和一个整数,其中第一个实数是汽车油箱的最大容量(单位:I。);第二个实数是汽车每升油能行驶的公里数;第三个实数是汽车在出发地加满油箱时的费用(单位元);一个整数是1到50间的数,表示从出发地到目的地线路上加油站的数目。
接下来n行都是两个实数,第一个数表示从出发地到某一个加油站的距离(单位:km);第二个实数表示该加油站汽油的价格(单位:元)
数据项中的每个数据都是正确的,不需判错。一条线路上的加油站根据其到出发地的距离递增排列并且都不会大于从出发地到目的地的距离。
【输出】
就一个数据,是精确到0.1元的最小的加油和吃饭费用
【样例】
样例输入
600
40 8.5 128 3
200 3.52
350 3.45
500 3.65
样例输出
379.6
具体解释见代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=50+5;
double dist,l,km,cost0,ans=1e30;
int n;
double d[maxn],cost[maxn];
void init()
{
freopen("input.txt","r",stdin);
}
void readdata()
{
scanf("%lf%lf%lf%lf%d",&dist,&l,&km,&cost0,&n);
for (int i=1;i<=n;i++)
{
scanf("%lf%lf",&d[i],&cost[i]);
}
d[0]=0;cost[0]=0;
d[n+1]=dist;cost[n+1]=0;
}
void dfs(int x,double oil,double money)//到达加油站x,还剩oil升油,已经花费了money
{
//printf("%d,%lf,%lf\n",x,oil,money);
if (oil<0) return;// 可行性剪枝
if(money>=ans) return;//最优化剪枝
if(x==n+1) //到达目标
{
if(ans>money) ans=money;
return;
}
double oiltouse=(d[x+1]-d[x])/km;//到达下一个加油站需要用的油
//不加油
dfs(x+1,oil-oiltouse,money);
if (oil>=l/2 && oil>=oiltouse) return;//不能加油
//加油
dfs(x+1,l-oiltouse,money+20+(l-oil)*cost[x]);
}
void work()
{
dfs(0,0,cost0-20);
printf("%.1lf\n",ans);
}
int main()
{
//init();
readdata();
work();
return 0;
}
T4:Lg P1189 SEARCH
非常值得一做!!!难度:★★★
几个问题:
1…怎么存图?这个简单一个字符一个字符读入,如果遇到‘.’mapp设为1表示当前可以走;遇到*同时将ans设为1,表示当前有可能走到这个位置
2.怎么保存方位?我们都用dx,dy规定方向,所以讲首字母大写与dx,dy相照应即可
2.如何移车?具体见代码dfs部分注释
#include <bits/stdc++.h>
using namespace std;
const int maxn= 55;
int n,m,k;
char st[maxn][maxn];
char dir[maxn];
int used[maxn][maxn],ans[maxn][maxn],mapp[maxn][maxn];
int dx[]={-1,1,0,0};//和NORTH,SOUTH,WEST,EAST一一对应,便于将字符转换成数字。
int dy[]={0,0,-1,1};
void init()
{
freopen("Lg P1189.in","r",stdin);
}
void dfs(int dir)
{
memset(used,0,sizeof(used)); //每次遍历之前当然要清0啦
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(ans[i][j] && !used[i][j])//当前的点有可能是车,那么就去移动这辆车
{
int nx=dx[dir]+i,ny=dy[dir]+j; //新坐标
ans[i][j]=0;//移走了过后就不在原来位置啦,所以讲原来的ans设为0
while(mapp[nx][ny])//可以走,将每一种情况走一边,也可以用dfs递归实现
{
used[nx][ny]=true;
ans[nx][ny]=1;
nx=nx+dx[dir];//下一个坐标,相当于递归
ny=ny+dy[dir];
}
}
}
}
}
void readdata()
{
scanf("%d%d\n",&n,&m);
//1..怎么存图?这个简单一个字符一个字符读入,如果遇到‘.’mapp设为1表示当前可以走;遇到*同时将ans设为1,表示当前有可能走到这个位置
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%c",&st[i][j]);
if(st[i][j]=='.')mapp[i][j]=1;
else if(st[i][j]=='*')ans[i][j]=1,mapp[i][j]=1;
else mapp[i][j]=0;
}
scanf("\n");
}
}
void work()
{
scanf("%d",&k);
for(int i=1;i<=k;i++)
{
scanf("%s",dir) ;//2.怎么保存方位?我们都用dx,dy规定方向,所以讲首字母大写与dx,dy相照应即可
if(dir[0]=='N')dfs(0); //0代表dx[0]和dy[0]以此类推
if(dir[0]=='S')dfs(1);
if(dir[0]=='W')dfs(2);
if(dir[0]=='E')dfs(3);
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(ans[i][j])printf("*") ;
else if(st[i][j]=='*')printf(".");//一定要注意这一点!!!
else printf("%c",st[i][j]);
}
printf("\n");
}
}
int main()
{
// init();
readdata();
work();
return 0;
}