BFS
题目6.3链接非常可乐
这道题是隐含的BFS题目,重点在于状态的构建和状态如何转化。状态就是定义的结构体,转化就是BFS遍历。具体思路参考书目。
代码如下:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
const int MAX = 101;
int visit[MAX][MAX][MAX];
int S, N, M, t[10];
struct Node
{
int a, b, c, t;
Node(int a, int b, int c, int t):a(a), b(b), c(c), t(t){}
};
queue<Node> q;
void AtoB(int &a, int &b, int sb)//A中的倒进B中
{
if(a>sb-b)
{
a = a-(sb-b);
b = sb;
}
else
{
b += a;
a = 0;
}
}
void bfs()
{
memset(visit, 0, sizeof(visit));
while(!q.empty()) q.pop();
q.push(Node(S, 0, 0, 0));
visit[S][0][0] = 1;
while(!q.empty())
{
Node u = q.front(); q.pop();
//cout << u.a << " " << u.b << " " << u.c << " " << u.t << endl;
if((u.b==S/2 && u.c==S/2 && u.a==0) || (u.a==S/2 && u.b==S/2 && u.c==0) || (u.a==S/2 && u.c==S/2 && u.b==0))//要保证绝对分半
{
cout << u.t << endl;
return;
}
t[0] = u.a, t[1] = u.b, t[2] = u.c;
t[3] = S, t[4] = N, t[5] = M;//记录每个杯子的状态和其最大容量,方便一下遍历
for(int i=0; i<3; i++)
{
for(int j=0; j<3; j++)
{
if(i==j) continue;
if(t[i]==0) continue; //t[i]为空
if(t[j]==t[j+3]) continue;//t[j]已满
int ta = t[i], tb = t[j];
AtoB(t[i], t[j], t[j+3]);
if(visit[t[0]][t[1]][t[2]]==0)
{
visit[t[0]][t[1]][t[2]] = 1;
q.push(Node(t[0], t[1], t[2], u.t+1));
}
t[i] = ta, t[j] = tb;
}
}
}
cout << "NO" << endl;
return;
}
int main()
{
while(scanf("%d%d%d", &S, &N, &M)!=EOF && S)
{
bfs();
}
return 0;
}
递归和回溯
素数环
题目链接:素数环
题目大意:求出给定数的素数环。
思路:递归的思路。设置visit数组来标记是否已经放问过,ans数组来存储结果,选取未访问的结点,并判断新的邻居之和是否为素数,是则存储,不是则重新选取。
代码如下:
#include <iostream>
#include <cstring>
using namespace std;
int ans[22];
int visit[22];
int n;
bool isPrime(int n)
{
for(int i=2; i*i<=n; i++)
{
if(n%i==0)
return 0;
}
return 1;
}
void Print(int n)
{
for(int i=1; i<=n; i++)
{
if(i!=1) cout << " ";
cout << ans[i];
}
cout << endl;
}
void Ring(int num)
{
if(num>1)
{
if(isPrime(ans[num]+ans[num-1])==0)
return;
}
if(num==n)
{
if(isPrime(ans[num]+ans[1])==0)
return;
Print(num);
}
for(int i=2; i<=n; i++)
{
if(visit[i]==0)
{
visit[i] = 1;
ans[num+1] = i;
Ring(num+1);
visit[i] = 0;
}
}
}
int main()
{
int T = 1;
while(cin >> n)
{
memset(visit, 0, sizeof(visit));
visit[1] = 1;
ans[1] = 1;
cout << "Case " << T++ << ":" << endl;
Ring(1);
cout << endl;
}
return 0;
}
方法二:这个方法简单易理解
代码如下:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define inf 0x3f3f3f3f
#define MAX 22
#define repf(i,from ,to) for(int i =from ; i <to ; i++)
using namespace std;
int visit[MAX];
int n;
int num[MAX];
bool isPrime(int a )
{
if(a==1)
return false ;
for(int i = 2;i<=sqrt(a);i++)
{
if(a%i==0)
{
return false ;
}
}
return true ;
}
//迭代深搜
void dfs(int step)
{
if(step>n&&isPrime(num[n]+num[1]))
{
for(int i = 1 ; i <n ; i ++)
cout << num[i]<<" ";
cout << num[n]<<endl;
}
for(int i = 2 ; i <=n ; i ++)
{
num[step]=i;
if(isPrime(num[step]+num[step-1])&&!visit[i])
{
visit[i]=1;
dfs(step+1);
visit[i]=0;
}
}
}
int main(){
int c = 1 ;
while( cin >> n )
{
cout<<"Case "<<c++<<":"<<endl;
memset(num,0,sizeof(num));
num[1] = 1;
dfs(2);
cout <<endl;
}
return 0 ;
}
全排列
题目链接:全排列
题目大意:输出给定字符序列的全排列。
思路:回溯遍历。
代码如下:
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAX = 10;
char s[MAX], q[MAX];
int visit[MAX], m;
void DFS(char s[], int t){
q[m++] = s[t];
if(m==strlen(s)){
printf("%s\n", q);
return;
}
for(int j=0; j<strlen(s); j++){
if(visit[j]==0){
visit[j] = 1;
DFS(s, j);
m--;
visit[j] = 0;
}
}
}
int main(){
while(scanf("%s", s)!=EOF){
int len = strlen(s);
sort(s, s+len);
for(int i=0; i<len; i++){
m = 0;
memset(visit, 0, sizeof(visit));
visit[i] = 1;
DFS(s, i);
}
cout << endl;
}
return 0;
}
带减枝的DFS
题目链接:Tempter of the Bone
参考博客:HDU 1010 && ZOJ 2110–Tempter of the Bone【DFS && 奇偶剪枝】
题目大意:在规定的步数时要到达目的地。
思路:需要在DFS的过程中加入减枝(包括奇偶减枝:超出最短路径的部分一定是偶数)。
代码:
#include <iostream>
#include <cmath>
using namespace std;
int N, M, T, sx, sy, ex, ey;
char map[6][6];
const int dir[4][2] = { { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 } };
bool solved = false, arrd[6][6];
int Distance ( int x, int y )
{
return abs ( (double)x - ex ) + abs ( (double)y - ey ); // 当前点(x,y)到终点(ex,ey)的最短距离
}
void DFS ( int x, int y, int step )
{
if ( solved ) return;
if ( map[x][y] == 'D' && step == T ) {
solved = true;
return;
}
if ( step >= T ) return; // 当前时间即步数(step) >= T 而且还没有找到D点
int dis = T - step - Distance ( x, y );
if ( dis < 0 || dis % 2 ) return; // 剩余步数小于最短距离或者满足奇偶剪枝条件
for ( int i = 0; i < 4; i += 1 ) {
int tx = x + dir[i][0];
int ty = y + dir[i][1];
int tstep = step + 1;
if ( tx >= 0 && tx < N && ty >= 0 && ty < M && map[tx][ty] != 'X' && !arrd[tx][ty]) {
arrd[tx][ty] = true;
DFS ( tx, ty, tstep );
arrd[tx][ty] = false;
}
}
}
int main ( int argc, char *argv[] )
{
while ( cin >> N >> M >> T, N+M+T ) {
solved = false;
int xnum = 0; // 记录'X'的数量
for ( int i = 0; i < N; i += 1 ) {
cin.get();
for ( int j = 0; j < M; j += 1 ) {
cin >> map[i][j];
arrd[i][j] = false;
if ( map[i][j] == 'S' ) {
sx = i;
sy = j;
arrd[i][j] = true;
}
else if ( map[i][j] == 'D' ) {
ex = i;
ey = j;
}
else if ( map[i][j] == 'X' ) {
xnum++;
}
}
}
if ( N * M - xnum > T ) { // 可通行的点必须大于要求的步数,路径剪枝。
DFS ( sx, sy, 0 );
}
if ( solved )
cout << "YES" << endl;
else
cout << "NO" << endl;
}
return 0;
}
书上的代码:
#include <cstdio>
using namespace std;
char maze[8][8];
int n, m, t;
int sucess;
int go[][2] = {1, 0, -1, 0, 0, 1, 0, -1};
void DFS(int x, int y, int time){
if(sucess) return;
for(int i=0; i<4; i++){
int nx = x + go[i][0];
int ny = y + go[i][1];
if(nx<=0 || nx>n || ny<=0 || ny>m) continue;
if(maze[nx][ny]=='X') continue;
if(maze[nx][ny]=='D'){
if(time+1==t){
sucess = 1;
return;
}else{
continue;
}
}
maze[nx][ny] = 'X';
DFS(nx, ny, time+1);
maze[nx][ny] = '.';
}
}
int main(){
while(scanf("%d%d%d", &n, &m, &t) && n){
for(int i=1; i<=n; i++){
scanf("%s", maze[i]+1);
}
sucess = 0;
int sx, sy;
for(int i=1; i<=n; i++){
for(int j=1; j<=m; j++){
if(maze[i][j]=='D'){
sx = i, sy = j;
}
}
}
for(int i=1; i<=n; i++){
for(int j=1; j<=m; j++){
if(maze[i][j]=='S' && (i+j)%2==((sx+sy)%2+t)%2){
maze[i][j] = 'X';
DFS(i, j, 0);
}
}
}
if(sucess) printf("YES\n");
else printf("NO\n");
}
return 0;
}