Virtual Oj搜索专题练习 DFS BFS 剪枝 回溯 题解总结 “穷竭搜索”

23 篇文章 0 订阅
22 篇文章 0 订阅

                                                                         -----石泉 组题

A. HDU 1035 Robot Motion

原地址 : http://acm.hdu.edu.cn/showproblem.php?pid=1035
题目::
在这里插入图片描述
A robot has been programmed to follow the instructions in its path. Instructions for the next direction the robot is to move are laid down in a grid. The possible instructions are
N north (up the page)
S south (down the page)
E east (to the right on the page)
W west (to the left on the page)
For example, suppose the robot starts on the north (top) side of Grid 1 and starts south (down). The path the robot follows is shown. The robot goes through 10 instructions in the grid before leaving the grid.
Compare what happens in Grid 2: the robot goes through 3 instructions only once, and then starts a loop through 8 instructions, and never exits.
You are to write a program that determines how long it takes a robot to get out of the grid or how the robot loops around.
Input
There will be one or more grids for robots to navigate. The data for each is in the following form. On the first line are three integers separated by blanks: the number of rows in the grid, the number of columns in the grid, and the number of the column in which the robot enters from the north. The possible entry columns are numbered starting with one at the left. Then come the rows of the direction instructions. Each grid will have at least one and at most 10 rows and columns of instructions. The lines of instructions contain only the characters N, S, E, or W with no blanks. The end of input is indicated by a row containing 0 0 0.
Output
For each grid in the input there is one line of output. Either the robot follows a certain number of instructions and exits the grid on any one the four sides or else the robot follows the instructions on a certain number of locations once, and then the instructions on some number of locations repeatedly. The sample input below corresponds to the two grids above and illustrates the two forms of output. The word “step” is always immediately followed by “(s)” whether or not the number before it is 1.
Sample Input
3 6 5
NEESWE
WWWESS
SNWWWW
4 5 1
SESWE
EESNW
NWEEN
EWSEN
0 0
Sample Output
10 step(s) to exit
3 step(s) before a loop of 8 step(s)


题意::
一个机器人在一个n*m的方格内移动,方格内每个点都有意条指令N 向上走一步,S向下走一步,W向左走一步,E向`走一步。以第一行为行 输入n,m,s.way[1][s],为机器人的起始位置,
问机器人多少步能走出这个方格cout<<10 step(s) to exit,如果重复走同一格cout<<3 step(s) before a loop of 8 step(s) .
解题思路::
用DFS来遍历每一个节点,并且记录是否遍历过vis[i][j]=setp,判断边界条件,不重复满足输。重复求前当前的步数当前step-vis[i][j]. 主要DFS遍历 并且判断边界条件。

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
char str[999][999];
int a,b,s;
int step;
int vis[999][999]= {0};// 初始化为0;
void DFS(int i,int j)
{
    if(i<=0||i>a||j<=0||j>b) {    // 超出边界 停止输出
        cout<<step<<" step(s) to exit"<<endl;
        return ;
    }
    step++;      遍历一个步数+1if(vis[i][j]>0) {          // 判断是否重复遍历过
        int k=vis[i][j];
        cout<<k-1<<" step(s) before a loop of "<<step-k<<" step(s)"<<endl;
        return ;
    }
    vis[i][j]=step;            //判断四个方向,判断该往哪个方向移动 
    if(str[i][j]=='W')
        DFS(i,j-1);
    else if(str[i][j]=='S')
        DFS(i+1,j);
    else if(str[i][j]=='N')
        DFS(i-1,j);
    else if(str[i][j]=='E')
        DFS(i,j+1);
}
int main()
{
    while(cin>>a>>b) {
        if(a==0&&b==0)
            break;
        step=0;//初始化步数为0;
        cin>>s;
        memset(vis,0,sizeof(vis));
        int i,j;
        for(i=1; i<=a; i++)
            for(j=1; j<=b; j++)
                cin>>str[i][j];
        DFS(1,s);   //  由第一行第s列开始
    }
    return 0;
}

B. OpenJ_Bailian 2748 全排列

原地址: http://bailian.openjudge.cn/practice/2748?lang=en_US
题目::
给定一个由不同的小写字母组成的字符串,输出这个字符串的所有全排列。 我们假设对于小写字母有’a’ < ‘b’ < … < ‘y’ < ‘z’,而且给定的字符串中的字母已经按照从小到大的顺序排列。
Input
输入只有一行,是一个由不同的小写字母组成的字符串,已知字符串的长度在1到6之间。
Output
输出这个字符串的所有排列方式,每行一个排列。要求字母序比较小的排列在前面。字母序如下定义:
已知S = s 1s 2…s k , T = t 1t 2…t k,则S < T 等价于,存在p (1 <= p <= k),使得
s 1 = t 1, s 2 = t 2, …, s p - 1 = t p - 1, s p < t p成立。
Sample Input
abc
Sample Output
abc
acb
bac
bca
cab
cba

dfs+奇偶剪枝+回溯
解题思路::常规的dfs题型写法,需要注意剪枝。第一步剪枝就是判断全都是路的情况给的时间都小于最短到达的时间,就不用搜索判断一下直接输出;第二步奇偶剪枝比较难发现,搜索过程中剩余时间(t-step)减去到达终点的所需时间为奇数时就不可能到达终点。

//这个题解copy别人的 自己要看懂。
/*OpenJ_Bailian 2748*/
#include<bits/stdc++.h>
using namespace std;
char s[10];
int num=0;
char mapp[10];
int vis[10];
void dfs(char a)
{
	if(num==strlen(s)-1 ) //如果num与s的长度相等 表示搜索到头了 
	{
		for(int i=0; i<=num; i++)// 遍历输出mapp数组然后返回
		{
			cout<<mapp[i];
		}
		putchar(10);//输出回车
		return;
	}
	for(int i=0; i<strlen(s); i++) // i 从0开始按字典序对下一个字符进行深搜
	{
		if(vis[i]==0)//如果s[i]没有被访问过就对是s[i]深搜
		{
			num++;
			vis[i]=1;//标记已访问
			mapp[num]=s[i]; //访问后把s[i]存储在mapp里
			dfs(s[i]);//对s[i]深搜
			vis[i]=0;//回溯
			num--;//同上
		}
	}
}
int main()
{
	while(cin>>s)
	{
		sort(s,s+strlen(s)); //从小到大给数组s排序
		for(int i=0; i<strlen(s); i++) // 按字典序挨个对s[i]深搜
		{
			num=0;// num存储是当前第几个数据
			mapp[0]=s[i]; //把深搜出来的字符存到mapp数组里
			vis[i]=1; //遍历后标记被访问过
			dfs(s[i]);//对s[i]深搜】
			memset(vis,0,sizeof(vis)); //深搜结束后把vis 和 mapp 清空
			memset(mapp,'0',sizeof(mapp));
		}
	}
	return 0;
}

自己AC代码::
解题思路
::利用c++中STL中的函数全排列next_permutation(str,str+n)重下标为0到n-1的全排列函数实现。

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
char str[10];
int main()
{
    int n;
    while(scanf("%s",str)!=EOF)//录入字符串
    {
        n=strlen(str);
        do  //循环输出
        {  printf("%s\n",str);
        }while(next_permutation(str,str+n));
    }
    return 0;
}
//OK

C. HDU 1010 Tempter of the Bone

原地址: http://acm.hdu.edu.cn/showproblem.php?pid=1010
题目::
The doggie found a bone in an ancient maze, which fascinated him a lot. However, when he picked it up, the maze began to shake, and the doggie could feel the ground sinking. He realized that the bone was a trap, and he tried desperately to get out of this maze.
The maze was a rectangle with sizes N by M. There was a door in the maze. At the beginning, the door was closed and it would open at the T-th second for a short period of time (less than 1 second). Therefore the doggie had to arrive at the door on exactly the T-th second. In every second, he could move one block to one of the upper, lower, left and right neighboring blocks. Once he entered a block, the ground of this block would start to sink and disappear in the next second. He could not stay at one block for more than one second, nor could he move into a visited block. Can the poor doggie survive? Please help him.
Input
The input consists of multiple test cases. The first line of each test case contains three integers N, M, and T (1 < N, M < 7; 0 < T < 50), which denote the sizes of the maze and the time at which the door will open, respectively. The next N lines give the maze layout, with each line containing M characters. A character is one of the following:
‘X’: a block of wall, which the doggie cannot enter;
‘S’: the start point of the doggie;
‘D’: the Door; or
‘.’: an empty block.
The input is terminated with three 0’s. This test case is not to be processed.
Output
For each test case, print in one line “YES” if the doggie can survive, or “NO” otherwise.
Sample Input
4 4 5
S.X.
…X.
…XD

3 4 5
S.X.
…X.
…D
0 0 0
Sample Output
NO
YES

题意:
doggie 在一个古老的迷宫发现了一块骨头,他捡起骨头,发现是个陷阱,它中了陷阱,doggie想逃出迷宫,同时也知道地图,问你doggie能否逃出去?其中地图 S 为doggie的位置 ’ . '为可以走的路线,X 为doggie翻越不了的围墙,D是出口的门。 输入n*m大的迷宫 t为D门打开的时间点,输出NO / YES doggie 能否逃出去。
解题思路
DFS+回溯+剪枝 遍历每一个点,利用计数器step记录步数,也就是到达D的时间,判断边界条件 ,其中重点,记录X的个数 并且判断 这个式子if(t>n*m-sum-1) {cout<<"NO"<<endl; continue;} 直接判断,不然很容易TLE,好要记得的访问过,将这个点回溯,清除标记,以便于下一次遍历(回溯)。
AC代码::

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
int n,m,t;
char str[9][9];
int vis[9][9]= {0};
int dx,dy;
int g; // 思路正确 TLE 剪枝不够
void DFS(int x,int y,int step)//传入当前位置,和走的步数
{
    int fix[4][2]= {{0,1},{0,-1},{1,0},{-1,0}};//来个行走的方向
    if(x==dx&&y==dy) {//判断是否到达了位置
        if(t==step)   //时间是否刚好合适
            g=1;
        return ;  //按条件返回
    }
    if(g)
        return ;
    if(str[x][y]=='X')
        return ;  // 遇到X反回上一步操
    for(int i=0; i<4; i++) { //向四个方向遍历
        int fx=x+fix[i][0],fy=y+fix[i][1];
        if(fx<0||fx>=n||fy<0||fy>=m)//到达边界换一个方向
            continue;  //省的在DFS一次 减少时间
        if(vis[fx][fy]==0) {  //满足条件 向下遍历
            vis[fx][fy]=1;
            DFS(fx,fy,step+1);
            vis[fx][fy]=0;   并且回溯回去 还原标记
        }
    }
}
int main()
{
    while(cin>>n>>m>>t) {
        if(n==0&&m==0&&t==0)
            break;
        int x,y,sum=0;
        for(int i=0; i<n; i++)
            for(int j=0; j<m; j++) {
                cin>>str[i][j];
                if(str[i][j]=='S')// 找出doggie在的位置
                    x=i,y=j;
                if(str[i][j]=='D')//找出门在的位置
                    dx=i,dy=j;
                if(str[i][j]=='X')// 统计不可翻越的墙数量
                    sum++;
            }
        if(t>n*m-sum-1) {// d当开门时间大于 所有路的总合,不可逃出去
            cout<<"NO"<<endl;// 看来doggie相当的倒霉
            continue;}
        g=0; // 做个标记,能否逃出
        vis[x][y]=1;//否者将自己位置标记
        DFS(x,y,0);//DFS找出路
        vis[x][y]=0;// 清除某个点的标记
        if(g==0) // 判断是否在刚好的时间内到达指定地点  并且输出结果
            cout<<"NO"<<endl;
        else
            cout<<"YES"<<endl;
    }
    return 0;
}
// over

D. HDU 1016 素数环

原地址: http://acm.hdu.edu.cn/showproblem.php?pid=1016
题目::
A ring is compose of n circles as shown in diagram. Put natural number 1, 2, …, n into each circle separately, and the sum of numbers in two adjacent circles should be a prime.
Note: the number of first circle should always be 1.
在这里插入图片描述
Input
n (0 < n < 20).
Output
The output format is shown as sample below. Each row represents a series of circle numbers in the ring beginning from 1 clockwisely and anticlockwisely. The order of numbers must satisfy the above requirements. Print solutions in lexicographical order.
You are to write a program that completes above process.
Print a blank line after each case.
Sample Input
6
8
Sample Output
Case 1:
1 4 3 2 5 6
1 6 5 2 3 4

Case 2:
1 2 3 8 5 6 7 4
1 2 5 8 3 4 7 6
1 4 7 6 5 8 3 2
1 6 7 4 3 8 5 2

题意::
简单来说:让你输入一个数 n 从1——n 形成一个环 保证任意两个相, 邻的数的和为一个素数;例如;a[0]+a[1]——> 素数, a[0]+a[n-1]----->也是一个素数,而且 a[0]被限制只能为1.,并且输出所有能组成一个这样素数环的 数列 并且形如测试用例;(这里有一个小坑,就是输出每组的 可能中 最后一个数没有空格 要注意!!!)
解题思路
DFS+回溯。并且要判断可以组成环的可能是否为响铃两个数为素数;
这里n 只要求小于20的,直接列举出来就可以了。也就是一个板子;
AC代码::

#include <iostream>
//#include<bits/stdc++.h>
using namespace std;
int sum[99];
int vis[99]= {0};
int n;
int prim[12]= {2,3,5,7,11,17,13,19,23,29,31,37};
bool judge(int he)//  检查是否为素数 我列举了小于40的全部素数

{
    for(int i=0; i<12; i++)
        if(he==prim[i])
            return true;
    return false;
}
void DFS(int mum)
{
   //判断n个数是否安排完了,并且最后一个数和a[0]能否组成素数
    if(mum==n&&judge(sum[0]+sum[n-1])) { //边界条件的判断
        for(int i=0; i<n-1; i++)// 输出格式,不要被坑了
            cout<<sum[i]<<" ";
        cout<<sum[n-1];
        cout<<endl;
        return ;
    }
    for(int i=2; i<=n; i++) {  //遍历下一个数
        if(vis[i]==0&&judge(i+sum[mum-1])) { //判断下一个数是否合法
            sum[mum]=i; //是则归并进去
            vis[i]=1;//并设为标记过
            DFS(mum+1);
            vis[i]=0; //回溯回去
        }
    }
}
int main()
{
    int num=1;
    sum[0]=1;// 第一个数总是1;
    while(cin>>n) {
        cout<<"Case "<<num++<<":"<<endl;// 格式  汗颜呀!!
        DFS(1);
        cout<<endl; //  haha  还要保持格式
    }
    return 0;
}
//总之被坑了!!1

E. POJ 3278 Catch That Cow

原地址: http://poj.org/problem?id=3278
题目
Farmer John has been informed of the location of a fugitive cow and wants to catch her immediately. He starts at a point N (0 ≤ N ≤ 100,000) on a number line and the cow is at a point K (0 ≤ K ≤ 100,000) on the same number line. Farmer John has two modes of transportation: walking and teleporting.

  • Walking: FJ can move from any point X to the points X - 1 or X + 1 in a single minute
  • Teleporting: FJ can move from any point X to the point 2 × X in a single minute.
    If the cow, unaware of its pursuit, does not move at all, how long does it take for Farmer John to retrieve it?

Input
Line 1: Two space-separated integers: N and K
Output
Line 1: The least amount of time, in minutes, it takes for Farmer John to catch the fugitive cow.
Sample Input
5 17
Sample Output
4
Hint
The fastest way for Farmer John to reach the fugitive cow is to move along the following path: 5-10-9-18-17, which takes 4 minutes.
题意
说真的John真的这么多牛,整天都在找牛:也就是John这家伙的牛又丢了,他知道自己在什么位置n,而且它的牛在哪个位置k;而且牛不知道John在追它,所以他的位置不变(可能在专心的吃草)
而John每次只能三种走法1.向后走一步,2.向前走一步,2,一次性走到现在位置的2倍(魔鬼的步伐)。John 是个懒汉 想走最少的步子来追到牛,问你他最少走几步(自己数数不就好了嘛!!)。
解题思路
BFS 遍历,将每种情况都遍历一遍,加上限制条件,当n==k时跳出循环,并且记录步数,到达时返回步数就OK了。
AC代码::

#include <iostream>
#include<queue>
using namespace std;
const int maxn=1000005;
int hp[maxn]= {0};
int vis[maxn]= {0};
int just(int next)
{
    if(next<0||next>maxn||vis[next]==1)
        return 0;
    else
        return 1;
}
int BFS(int n,int k)
{
    queue<int> q;//用来存储下一个位置的节点
    int at,next;// 现在和下一个位置
    q.push(n);//现在的位置压入队列
    vis[0]=1; 
    while(!q.empty()) 
	{
        at=q.front();//得到现在的位置从队列取出元素给at
        q.pop();//清除队头元素
        for(int i=0; i<3; i++) { // 列举三种走法
            if(i==0) next=at-1;
            else if(i==1) next=at+1;
            else if(i==2) next=at*2;
            if(just(next)) {//判断next 是否合法 有没有被访问过
                q.push(next);//合法将其压入队列
                vis[next]=1;  //并且标记
                hp[next]=hp[at]+1;下一步等于 上一步+1}
            if(next==k)// d当next==k 追带牛了  返回到达next的步数
                return hp[next];
        }
    }
}
int main()
{
    int n,k;
    cin>>n>>k; // 人和牛的位置
    if(k<=n)cout<<n-k<<endl;  牛在人的左方 只能一步步走
    else cout<<BFS(n,k)<<endl;
    return 0;
}

F. HDU 1509 Windows Message Queue

原地址: http://acm.hdu.edu.cn/showproblem.php?pid=1509
题目
Message queue is the basic fundamental of windows system. For each process, the system maintains a message queue. If something happens to this process, such as mouse click, text change, the system will add a message to the queue. Meanwhile, the process will do a loop for getting message from the queue according to the priority value if it is not empty. Note that the less priority value means the higher priority. In this problem, you are asked to simulate the message queue for putting messages to and getting message from the message queue.
Input
There’s only one test case in the input. Each line is a command, “GET” or “PUT”, which means getting message or putting message. If the command is “PUT”, there’re one string means the message name and two integer means the parameter and priority followed by. There will be at most 60000 command. Note that one message can appear twice or more and if two messages have the same priority, the one comes first will be processed first.(i.e., FIFO for the same priority.) Process to the end-of-file.
Output
For each “GET” command, output the command getting from the message queue with the name and parameter in one line. If there’s no message in the queue, output “EMPTY QUEUE!”. There’s no output for “PUT” command.
Sample Input
GET
PUT msg1 10 5
PUT msg2 10 4
GET
GET
GET
Sample Output
EMPTY QUEUE!
msg2 10
msg1 10
EMPTY QUEUE!
题意
消息队列是windows系统的基本(basic fundamental)。对于每个程序,系统都会有一个消息队列。如果这个程序被操作,比如鼠标点击,文本(text)修改,系统将会给队列发送消息。与此同时,在非空的情况下,程序将会根据优先级值(priority value)从队列中不断得到消息。注意,优先级值越低表示的优先级越高。在这个问题中,你要模仿接收消息和发送消息的队列。
输入
输入项中只有一种情况。每一行是一个指令,接受或发送,即接收消息和发送消息。如果指令是发送,则会有一个表示消息名称的字符串还有两个整数,一个表示参数(parameter),一个表示优先权(priority)最多会有60000条指令。注意,一条消息能够出现两次或者更多,如果两条消息具有同样的优先权,那么先接收的那条将被执行(IE,FIFO都是这样的)。直至文件结束。
输出
对于每条接受指令,从消息队列中输出一行消息名和参数。如果在队列中没有消息,那么输出空队列。对于接受指令则没有输出。
解题思路
利用优先队列模拟排队信息, 5 和4 速效的优先级高 ,GET PUT 分别是输入,输出指令,可以用首字母判断,优先级高的先输出,,,用优先队列模拟排队 priority_queue q;
详细产考 :紫书 记录部分。
优先队列 感谢这位博主
AC代码::

#include<cstdio>
#include<queue>
using namespace std;
struct node
{
      int sign,vip,num;
      char mbr[400];

      bool friend operator < (node a,node b)
      {
            if (a.vip == b.vip)	
				return a.sign > b.sign;
            return a.vip > b.vip;    
      }
} ans;
int main()
{
      char get[4];
      priority_queue<node> q; //定义一个优先队列
      int k = 1;
      while (~scanf("%s",get)) //录入指令
            {
                  if (get[0] == 'G') //判断指令P/G
                        {
                              if (q.empty()) //判断并且输出
                                    {
                                          printf("EMPTY QUEUE!\n");
                                    }
                              else
                                    {
                                          ans = q.top();
                                          printf("%s %d\n",ans.mbr,ans.num);
                                          q.pop();
                                    }
                        }
                  else
                        {
                              scanf("%s %d %d",ans.mbr,&ans.num,&ans.vip);//指令输入
                              ans.sign = k;
                              k++;
                              q.push(ans);
                        }
            }
      return 0;
}

G. POJ 3669 Meteor Shower

原地址: http://poj.org/problem?id=3669
题目:
Bessie hears that an extraordinary meteor shower is coming; reports say that these meteors will crash into earth and destroy anything they hit. Anxious for her safety, she vows to find her way to a safe location (one that is never destroyed by a meteor) . She is currently grazing at the origin in the coordinate plane and wants to move to a new, safer location while avoiding being destroyed by meteors along her way.
The reports say that M meteors (1 ≤ M ≤ 50,000) will strike, with meteor i will striking point (Xi, Yi) (0 ≤ Xi ≤ 300; 0 ≤ Yi ≤ 300) at time Ti (0 ≤ Ti ≤ 1,000). Each meteor destroys the point that it strikes and also the four rectilinearly adjacent lattice points.
Bessie leaves the origin at time 0 and can travel in the first quadrant and parallel to the axes at the rate of one distance unit per second to any of the (often 4) adjacent rectilinear points that are not yet destroyed by a meteor. She cannot be located on a point at any time greater than or equal to the time it is destroyed).
Determine the minimum time it takes Bessie to get to a safe place.
Input

  • Line 1: A single integer: M
  • Lines 2…M+1: Line i+1 contains three space-separated integers: Xi, Yi, and Ti
    Output
  • Line 1: The minimum time it takes Bessie to get to a safe place or -1 if it is impossible.

Sample Input
4
0 0 2
2 1 2
1 1 2
0 3 5
Sample Output
5

题意:
一头羊(有个好听的没名字:贝茜),它听说地球会被流星M撞击,在吃草的它,为了活命,他决定逃到一个安全的地方(地球为什吗老是有灾难,为啥我没遇到),看报道它知道M流星将袭击某个点,时间是T,而且每颗流星都会摧毁它所撞击的点,以及四个直线相邻的方格点。贝西在时间为0时离开原点,可以在第一象限中以每秒1单位的速度平行于坐标轴移动到(通常是4)尚未被流星摧毁的相邻直线点上。她不能在任何大于或等于它被摧毁的时间点上定位)。确定贝茜到达安全地点的最短时间。(x,y,T)表示M流星撞击的位置(x,y)和时间T。
解题思路
BFS+优先队列模拟;
先用一个数组(final_map)存储 n个陨石过后形成的最终地图。
0 表示安全位置,输入陨石时用优先队列存储,按照陨石到达的时间给陨石排序。
然后从(0,0)位置广搜,搜到 final_map 中的 0 (安全位置)时直接结束。
搜索要按照时间根据实时地图(mapp)搜索,因为有些路是实时地图可以走的,而最终地图中的路被后来的陨石破坏。
搜索过程中高及时更新实时地图,当前位置的下一秒有陨石的话就在当前时间更新地图。因为你和陨石同时到达一个位置时,是行不通的。
如果搜不到安全位置就返回-1。(copy石泉)
AC代码::

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
#define mem(a) memset(a, 0, sizeof(a))
int final_map[550][550]; // 所有陨石到达后最终形成的地图
int mapp[550][550];      // 陨石坠落的实时地图
int vis[550][550];
int fx[4] = {0, 1, 0, -1};
int fy[4] = {1, 0, -1, 0};
struct ys //陨石到达的坐标时间
{
    int x, y, t;
};
struct now //当前坐标与时间
{
    int x, y, time;
};
struct cmp
{
    bool operator()(const ys &x, const ys &y)
    {
        return x.t > y.t; //陨石到达时间早的排在队首
    }
};
priority_queue<ys, vector<ys>, cmp> que; //创造一个按照陨石到达时间从小到大的优先级队列
void meteor(int x, int y, int ma[][550]) // 每颗陨石会对(x,y)上下左右造成陨石坑
{
    ma[x][y] = 1; // 1代表 不能走的路
    if (x - 1 >= 0) //地图的范围为 x,y>0
        ma[x - 1][y] = 1;
    if (y - 1 >= 0)
        ma[x][y - 1] = 1;
    ma[x][y + 1] = 1;
    ma[x + 1][y] = 1;
    return;
}
int bfs()
{
    mem(vis);
    queue<now> qq; //创建一个队列qq
    now fir, next;
    fir.x = 0;
    fir.y = 0;
    fir.time = 0;
    qq.push(fir); //把起点(0,0)入队
    while (!qq.empty())
    {
        ys to = que.top();  //  to 是最先到达的陨石
        now p = qq.front(); // p 是当前位置  接下来对 p 一周进行广搜
        qq.pop();
        if (final_map[p.x][p.y] == 0) //  如果在最终的地图上 点p 所在的位置是安全的 就表明到达安全位置
        {
            return p.time; // 输出 到达p点所用的时间
        }
        while (p.time == to.t - 1) //如果当前位置的下一秒 有陨石坠落 就修改实时地图
        {
            if (que.empty()) //如果 陨石队列为空   表示实时地图以及是最终的地图(final_map)就放心的走了(break)
                break;
            meteor(to.x, to.y, mapp); //否则 更新实时地图
            que.pop();//更新完让最前面的陨石出队 
            to = que.top(); //to指向下一个陨石  这里用while是因为可能有多个陨石同一时间坠落 
        }
        /*下面没什么好说的了 地图更新完后  常规对bfs做法*/
        for (int i = 0; i < 4; i++)
        {
            next.x = p.x + fx[i];
            next.y = p.y + fy[i];
            if (next.x >= 0 && next.x < 500 && next.y >= 0 && next.y < 500 && mapp[next.x][next.y] == 0 && vis[next.x][next.y] == 0)
            {
                next.time = p.time + 1;
                qq.push(next);
                vis[next.x][next.y] = 1;
            }
        }
    }
    return -1;//如果不能到达安全位置  就返回-1
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);

    mem(mapp);
    mem(final_map);
    int n;
    cin >> n;
    int x, y, t;
    ys p;
    while (n--)
    {
        cin >> x >> y >> t;
        meteor(x, y, final_map); //每次到达一颗陨石  final_map就修改一次  一直到所有的陨石都到达了  得出最终的地图(final_map)
        p.x = x;
        p.y = y;
        p.t = t;
        que.push(p); // 把陨石到达的坐标以及时间入队   队列中会按照到达的时间进行排序
    }
    int k = bfs();
    cout << k << endl;
    return 0;
}

DFS+剪枝。
将所有M流星袭击的点,及时间和相关的坐标有用二维数组记录,用vis记录地面有没有被袭击,ans保存并且返回答案。 判断边界是否和法。并且判断ans的值来判断是否能逃到安全的位置,if ans==2^32;–》-1 else —》ans 能逃出出的最短时间。

AC代码::

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int m,map[350][350],f[350][350],ans=2147483647;
void dfs(int x,int y,int t){
    if(map[x][y]<=t||t>=ans||t>=f[x][y]) //优化解决冗余
        return ;
    f[x][y]=t;
    if(map[x][y]>2100000000){//判断位置 找出最小解。
        ans=min(t,ans);
        return;
    } // 模拟逃亡的方向 
    dfs(x+1,y,t+1);
    if(x-1>=0)  dfs(x-1,y,t+1);//判断边界
    dfs(x,y+1,t+1);
    if(y-1>=0)  dfs(x,y-1,t+1);
}
int main(){
    memset(map,0x7f,sizeof(map));//初始化
    memset(f,0xa7f,sizeof(f));
    scanf("%d",&m);
    for(int i=1;i<=m;i++){
        int x,y,t;
        scanf("%d %d %d",&x,&y,&t);//二维数组模拟轰炸位置和时间
          //应为在一个坐标周围上可能被波及多次,所以取时间最短的一个记录。
        map[x][y]=min(map[x][y],t);  // 将轰炸的位置及时间 记录 map[x][y]=time;
        map[x+1][y]=min(map[x+1][y],t);  //取最小的time
        if(x-1>=0)  //判断边界, 一点周围相邻四个坐标
            map[x-1][y]=min(map[x-1][y],t);
        map[x][y+1]=min(map[x][y+1],t);
        if(y-1>=0)   //判断边界, 一点周围相邻四个坐标
            map[x][y-1]=min(map[x][y-1],t);
    }
    dfs(0,0,0);
    if(ans==2147483647)//如果没有逃脱
        printf("-1\n");
    else printf("%d\n",ans);
    return 0;
}
// 认真理解一下

H. HDU 1372 Knight Moves

原地址: http://acm.hdu.edu.cn/showproblem.php?pid=1372
题目:
A friend of you is doing research on the Traveling Knight Problem (TKP) where you are to find the shortest closed tour of knight moves that visits each square of a given set of n squares on a chessboard exactly once. He thinks that the most difficult part of the problem is determining the smallest number of knight moves between two given squares and that, once you have accomplished this, finding the tour would be easy.
Of course you know that it is vice versa. So you offer him to write a program that solves the “difficult” part.
Your job is to write a program that takes two squares a and b as input and then determines the number of knight moves on a shortest route from a to b.
Input
The input file will contain one or more test cases. Each test case consists of one line containing two squares separated by one space. A square is a string consisting of a letter (a-h) representing the column and a digit (1-8) representing the row on the chessboard.
Output
For each test case, print one line saying “To get from xx to yy takes n knight moves.”.
Sample Input
e2 e4
a1 b2
b2 c3
a1 h8
a1 h7
h8 a1
b1 c3
f6 f6
Sample Output
To get from e2 to e4 takes 2 knight moves.
To get from a1 to b2 takes 4 knight moves.
To get from b2 to c3 takes 2 knight moves.
To get from a1 to h8 takes 6 knight moves.
To get from a1 to h7 takes 5 knight moves.
To get from h8 to a1 takes 6 knight moves.
To get from b1 to c3 takes 1 knight moves.
To get from f6 to f6 takes 0 knight moves.
题意:
你的一个朋友正在研究旅行骑士问题(TKP),在这个问题中,你要找到最短的封闭的骑士移动之旅,它访问棋盘上给定的n个正方形中的每一个正方形,恰好一次。他认为这个问题最困难的部分是确定两个给定方块之间骑士移动的最小数量,一旦你完成了这个,找到路线就会很容易(骑士可能是骑着马,所以按日字的方式行走)输入两个坐标例如a1,b1;其中a代表行,1代表列,b1也是如此;让你找出,从一个坐标到另一个坐标最少需要多少步。输出例如:To get from e2 to e4 takes step_number knight moves.
解题思路:
BFS +方向
骑士走路不正常 日 字型步伐,所以定义走的八个方向,用二维数组记录到达那个点时走多少步pari< int int >p。来存储 x y;的坐标frist–>x second–>y;然后就是板子来套路解题了。
AC代码::

#include <iostream>
#include<queue>
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> p; // 存储X,Y的值也就是同时存坐标
// 定义骑士走的八个方向
int fix[8][2]= {{1,2},{2,1},{2,-1},{1,-2},{-1,-2},{-2,-1},{-2,1},{-1,2}};
int step[9][9];//  二维数组存储位置走的步数
int Bfs(int dx1,int dy1,int dx2,int dy2)
{
    queue<p> q; // 定义一的pari类型的的队列 选择pari为了方便
    q.push(p(dx1,dy1));// 压入到队列里,对应的坐标
    step[dx1][dy1]=1;//将第一步设置
    while(!q.empty()) {
        p fx=q.front();
        if(fx.first==dx2&&fx.second==dy2)// /满足到达位置是,返回步数
            return step[dx2][dy2];
        for(int i=0; i<8; i++) {
            int x=fx.first+fix[i][0];  //方向遍历  八个方向。
            int y=fx.second+fix[i][1];
            if(x>=1&&x<=8&&y>=1&&y<=8&& !step[x][y]) {// 判断是否超边际,有没有走过
                q.push(p(x,y));  // 将相应的坐标压入队列
                step[x][y]=step[fx.first][fx.second]+1;// 达到下一个坐标,步数加1。
            }
        }
        q.pop();  // 为力结束循环避免冗余。
    }
    return 0;
}
int main()
{
    int a,b,c,d;
    char str1[3];
    char str2[3];
    while(cin>>str1>>str2) {
        memset(step,0,sizeof(step));//初始化数组
        b=str1[0]-'a'+1;   //解析两个坐标
        a=str1[1]-'0';
        d=str2[0]-'a'+1;
        c=str2[1]-'0';
//        cout<<dx1<<" "<<dx2<<" " <<dy1<<" " <<dy2;
        int steps=Bfs(a,b,c,d); 
//        cout<<"To get from "<<str1<<" to "<<str2<<" takes "<<steps-1<<" knight moves."<<endl;
        printf("To get from %s to %s takes %d knight moves.\n",str1,str2,steps-1);
    }
    return 0;
}
// BFS 板子套一下就可以了。

I. HDU 1253 胜利大逃亡

原地址: http://acm.hdu.edu.cn/showproblem.php?pid=1253
题目:
Ignatius被魔王抓走了,有一天魔王出差去了,这可是Ignatius逃亡的好机会.
魔王住在一个城堡里,城堡是一个ABC的立方体,可以被表示成A个B*C的矩阵,刚开始Ignatius被关在(0,0,0)的位置,离开城堡的门在(A-1,B-1,C-1)的位置,现在知道魔王将在T分钟后回到城堡,Ignatius每分钟能从一个坐标走到相邻的六个坐标中的其中一个.现在给你城堡的地图,请你计算出Ignatius能否在魔王回来前离开城堡(只要走到出口就算离开城堡,如果走到出口的时候魔王刚好回来也算逃亡成功),如果可以请输出需要多少分钟才能离开,如果不能则输出-1.
在这里插入图片描述
Input
输入数据的第一行是一个正整数K,表明测试数据的数量.每组测试数据的第一行是四个正整数A,B,C和T(1<=A,B,C<=50,1<=T<=1000),它们分别代表城堡的大小和魔王回来的时间.然后是A块输入数据(先是第0块,然后是第1块,第2块…),每块输入数据有B行,每行有C个正整数,代表迷宫的布局,其中0代表路,1代表墙.(如果对输入描述不清楚,可以参考Sample Input中的迷宫描述,它表示的就是上图中的迷宫)
特别注意:本题的测试数据非常大,请使用scanf输入,我不能保证使用cin能不超时.在本OJ上请使用Visual C++提交.
Output
对于每组测试数据,如果Ignatius能够在魔王回来前离开城堡,那么请输出他最少需要多少分钟,否则输出-1.
Sample Input
1
3 3 4 20
0 1 1 1
0 0 1 1
0 1 1 1
1 1 1 1
1 0 0 1
0 1 1 1
0 0 0 0
0 1 1 0
0 1 1 0
Sample Output
11

解题思路
DFS+回溯+剪枝
想到能不能逃出去,肯定是用最短时间到达门口,魔王有没有回到家,也就是求到达门口的最短时间问题。应为是一个空间上的问题,所以我用了 三维数组(第一次用,和二维数组差不多)套DFS的板子。
AC代码::

#include <iostream>
#include <string.h>
#include <stdio.h>
int max_time;
int way[60][60][60];//定义地图数组
int vis[60][60][60];// 定义 标记数组
int a,b,c,t;
int num_step=0;定义一个最大步数
void DFS(int x,int y,int z,int step)//  dfs 板子 
{
    int fix[6][3]= {{1,0,0},{0,1,0},{0,0,1},{0,0,-1},{-1,0,0},{0,-1,0}};
    if(step>t||step>max_time||(a+b+c-step-x-y-z>t))return ; //(a+b+c-step-x-y-z>t)
    if(x==a-1&&y==b-1&&z==c-1) {     //剪枝过程
        max_time=step;
        return ;
    } 
    for(int i=0; i<6; i++) {
        int dx=x+fix[i][0],dy=y+fix[i][1],dz=z+fix[i][2];
        if(dx<0||dy<0||dz<0||dx>=a||dy>=b||dz>=c)   //换个方向搜索
            continue;
        if(way[dx][dy][dz]==0&&(vis[dx][dy][dz]==0||vis[dx][dy][dz]>step)) {
            vis[dx][dy][dz]=step;
            way[dx][dy][dz]=1;   // 更新步数
            DFS(dx,dy,dz,step+1);
            way[dx][dy][dz]=0;   // 回溯
        }
    }
}
int main()
{
    int K;
    scanf("%d",&K);
    while(K--) {
        max_time=1001;
        scanf("%d %d %d %d",&a,&b,&c,&t);
        for(int i=0; i<a; i++)for(int j=0; j<b; j++)for(int k=0; k<c; k++)scanf("%d",&way[i][j][k]);
        //cin>>way[i][j][k];
        memset(vis,0,sizeof(vis));
        DFS(0,0,0,0);//x,y,z,step;// 步数代表走的时间
        if(max_time==1001)
            printf("-1\n");
        else
            printf("%d\n",max_time);
    }
    return 0;
}
// DFS板子

J. HDU 1495 非常可乐

原地址: http://acm.hdu.edu.cn/showproblem.php?pid=1495
题目:
大家一定觉的运动以后喝可乐是一件很惬意的事情,但是seeyou却不这么认为。因为每次当seeyou买了可乐以后,阿牛就要求和seeyou一起分享这一瓶可乐,而且一定要喝的和seeyou一样多。但seeyou的手中只有两个杯子,它们的容量分别是N 毫升和M 毫升 可乐的体积为S (S<101)毫升 (正好装满一瓶) ,它们三个之间可以相互倒可乐 (都是没有刻度的,且 S==N+M,101>S>0,N>0,M>0) 。聪明的ACMER你们说他们能平分吗?如果能请输出倒可乐的最少的次数,如果不能输出"NO"。
Input
三个整数 : S 可乐的体积 , N 和 M是两个杯子的容量,以"0 0 0"结束。
Output
如果能平分的话请输出最少要倒的次数,否则输出"NO"。
Sample Input
7 4 3
4 1 3
0 0 0
Sample Output
NO
3
解题思路
题意:三个杯子中的可乐互相倒,最终有两个杯子可乐量相同,并且另外一个杯子为空表明操作成功。
思路:这道题说了要找最少步数,就应该想到用BFS。
三个杯子互相倒就会有 3! = 6 种操作:

k1->k2 
k1->k3
k2->k1
k2->k3
k3->k1
k3->k2

然后模拟互相倒水时,杯子中可乐的变化就行了
具体解释看代码中的注释,写出来一种操作,其他5种类似。
AC代码:

/*HDU 1495*/
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
typedef long long ll;
#define mem(a) memset(a,0,sizeof(a))
int s,n,m;
int vis[105][105][105];//标记这种操作是否用过
struct node
{
    int k1;// s杯中的量
    int k2;// n杯中的量
    int k3;// m杯中的量
    int step;
};
int bfs()
{
    mem(vis);
    queue<node> que;
    node fir,p,next;

    fir.k1=s; //刚开始 S为满的  n和m都是空的
    fir.k2=0;
    fir.k3=0;
    fir.step=0;
    que.push(fir);
    while(!que.empty())
    {
        p=que.front();
        que.pop();

        if((p.k1==p.k2 && p.k3==0) || (p.k1==p.k3 && p.k2==0) || (p.k2==p.k3 && p.k1==0)) //当有两个杯子的可乐相同并且另外一个杯子为空时完成任务
        {
            return p.step;//返回所花的步数
        }
        for(int i=1;i<=6;i++)     // 共有6种操作:1->2,  1->3,   2->1,   2->3,   3->1,   3->2
        {
            if(i==1)  //   k1->k2
            {
                if(p.k1+p.k2<=n) //k1 往 k2里到  k1+k2还小于 n  表示倒不满
                {
                    next.k1=0;  // n 倒不满 说明 s已经为空 
                    next.k2=p.k1+p.k2; // k2 的量为k1所有的加上k2原来有的
                    next.k3=p.k3; // k3没有操作  还是原来的量
                }
                else  //  n 倒满了 s中还有剩余 (k1>=0)
                {
                    next.k2=n;  // n 倒满了 所以k2就是最大量 n
                    next.k1=p.k1+p.k2-n; //  s->n的量为(n-k2) 所以 s中剩余的量为 k1-(n-k2)
                    next.k3=p.k3;
                }
            }
            if(i==2) // k1->k3
            {
                if(p.k1+p.k3<=m) 
                {
                    next.k1=0;
                    next.k3=p.k1+p.k3;
                    next.k2=p.k2;
                }
                else
                {
                    next.k3=m;
                    next.k1=p.k1+p.k3-m;
                    next.k2=p.k2;
                }
            }
            if(i==3) // k2->k1
            {
                if(p.k1+p.k2<=s) 
                {
                    next.k2=0;
                    next.k1=p.k1+p.k2;
                    next.k3=p.k3;
                }
                else
                {
                    next.k1=s;
                    next.k2=p.k1+p.k2-s;
                    next.k3=p.k3;
                }
            }
            if(i==4) // k2->k3
            {
                if(p.k2+p.k3<=m) 
                {
                    next.k2=0;
                    next.k3=p.k2+p.k3;
                    next.k1=p.k1;
                }
                else
                {
                    next.k3=m;
                    next.k2=p.k2+p.k3-m;
                    next.k1=p.k1;
                }
            }
            if(i==5) // k3->k1
            {
                if(p.k1+p.k3<=s) 
                {
                    next.k3=0;
                    next.k1=p.k1+p.k3;
                    next.k2=p.k2;
                }
                else
                {
                    next.k1=s;
                    next.k3=p.k1+p.k3-s;
                    next.k2=p.k2;
                }
            }
            if(i==6) // k3->k2
            {
                if(p.k3+p.k2<=n) 
                {
                    next.k3=0;
                    next.k2=p.k3+p.k2;
                    next.k1=p.k1;
                }
                else
                {
                    next.k2=n;
                    next.k3=p.k3+p.k2-n;
                    next.k1=p.k1;
                }
            }
            if(vis[next.k1][next.k2][next.k3]==0) // 如果这种操作没有操作过的话
            {
                vis[next.k1][next.k2][next.k3]=1; //标记已操作
                next.step=p.step+1;// 步数+1
                que.push(next);
            }
        }
    }
    return -1;//队空后还没有找到 合适的ans就返回-1
}
int main()
{
    while(cin>>s>>n>>m && s)
    {
        if(s&1) //如果S为奇数 直接输出NO
        {
            cout<<"NO"<<endl;
            continue;
        }
       int ans = bfs();
       if(ans!=-1) // 如果ans不等于-1 说明操作成功
       cout<<ans<<endl;
       else
       cout<<"NO"<<endl;
    }
    return 0;
}

数论代码:

/*HDU 1495*/
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#define INF 1<<30
#define N 210
using namespace std;
int gcd(int a,int b)
{
    if(a%b==0)
        return b;
    return gcd(b,a%b);
}
int main()
{
    int s,n,m;
    while(1)
    {
        scanf("%d%d%d",&s,&n,&m);
        if(s==0&&n==0&&m==0)
            break;
        if(s%2)
        {
            printf("NO\n");
            continue;
        }
        s=s/gcd(s,gcd(n,m));
        if(s%2)
            printf("NO\n");
        else
            printf("%d\n",s-1);
    }
    return 0;
}
//突然有个想法  二分能不能作  思考一下

加油!!^_^ ^_^!!!!!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值