DFS一般使用递归实现
深度优先算法解决背包问题
#define _CRT_SECURE_NO_WARNINGS 1 #include<cstdio> const int maxn = 30; int n, V, maxValue = 0; int w[maxn], c[maxn]; void DFS(int index, int sumW, int sumC) { if (index == n)//死胡同 { if (sumW <= V && sumC >= maxValue) { maxValue = sumC; } return; } //岔道口 DFS(index + 1, sumW, sumC);//不选第index件物品 DFS(index + 1, sumW + w[index], sumC + c[index]);//选第index件物品 } int main() { scanf("%d %d",&n,&V);//物品件数,背包总容量 for (int i = 0; i < n; i++) { scanf("%d", &w[i]); } for (int i = 0; i < n; i++) { scanf("%d",&c[i]); } DFS(0,0,0); printf("%d\n",maxValue); return 0; }
背包问题减枝
void DFS(int index, int sumW, int sumC) { if (index == n)//死胡同 { return; } //岔道口 DFS(index + 1, sumW, sumC);//不选第index件物品 if (sumW + w[index] <= V)//选index的时候有条件 { if (sumC + c[index] > maxValue) maxValue = sumC + c[index]; DFS(index + 1, sumW + w[index], sumC + c[index]);//选第index件物品 } }
背包问题减枝2.0
void DFS(int index, int sumW, int sumC) { if (index == n)//死胡同 { if (sumC >= maxValue) { maxValue = sumC; } return; } //岔道口 DFS(index + 1, sumW, sumC);//不选第index件物品 if (sumW + w[index] <= V)//选index的时候有条件 { DFS(index + 1, sumW + w[index], sumC + c[index]);//选第index件物品 } }
刚刚没找到codeup网址:在这里记录一下
http://codeup.hustoj.com/contest.php
问题 A: 【递归入门】全排列
#include <cstdio> int n; bool flag[20]={false}; int ans[20]; void combine(int count) { if(count==n) { for(int i=0;i<n;i++) { printf("%d ",ans[i]); } printf("\n"); return ; } for(int i=0;i<n;i++) { if(flag[i]==false) { ans[count]=i+1; flag[i]=true; combine(count+1); flag[i]=false; } } } int main() { while(~scanf("%d",&n)) { int count=0; combine(count); } return 0; }
问题 B: 【递归入门】组合的输出
#include <cstdio> using namespace std; int n, r; int ans[26]; bool flag[26] = { false }; void combine(int x, int count) { if (count == r) { for (int i = 0; i < r; i++) { printf("%d ", ans[i]); } printf("\n"); return; } for (int i = x; i <= n; i++) { if (flag[i - 1] == false) { ans[count] = i; flag[i - 1] = true; combine(i, count + 1); flag[i - 1] = false; } } } int main() { while (~scanf("%d %d", &n, &r)) { combine(1, 0); } return 0; }
问题 C: 【递归入门】组合+判断素数
#include <cstdio> #include <cmath> using namespace std; int n,k,count; int number[25],sum; bool isPrime(int n) { if(n<=1) return false; int sqr=(int)sqrt(1.0*n); for(int i=2;i<=sqr;i++) { if(n%i==0) return false; } return true; } void combine_judge(int pos,int level) { if(level==k) { if(isPrime(sum)==true) { count++; } return; } for(int i=pos;i<n;i++) { sum+=number[i]; combine_judge(i+1,level+1); sum-=number[i]; } } int main() { while(~scanf("%d %d",&n,&k)) { for(int i=0;i<n;i++) { scanf("%lld",&number[i]); } count=0; sum=0; combine_judge(0,0); printf("%d",count); } return 0; }
问题 D: 【递归入门】n皇后 问题(原始的8皇后问题)
#include <cstdio> int n,result[15],count; void n_queen(int index) { if(index==n) { for(int i=0;i<n;i++) { printf("%d ",result[i]+1); } printf("\n"); count++; return; } bool flag[15]={false}; for(int i=0;i<index;i++) { flag[result[i]]=true; int lower_right=i+result[i]-index; int top_right=-i+result[i]+index; if(lower_right>=0) flag[lower_right]=true; if(top_right<=n-1) flag[top_right]=true; } for(int i=0;i<n;i++) { if(flag[i]==false) { result[index]=i; n_queen(index+1); } } } int main() { while(~scanf("%d",&n)) { count=0; n_queen(0); if(count==0) printf("no solute!\n"); } return 0; }
问题 E: 【递归入门】出栈序列统计
#include <cstdio> int n,count; void dispose(int num,int pop,int push) { if(pop==0&&push==0) { count++; return; } if(push>0) dispose(num+1,pop,push-1); if(pop>0&&num>0) dispose(num-1,pop-1,push); } int main() { while(~scanf("%d",&n)) { count=0; dispose(0,n,n); printf("%d\n",count); } return 0; }
问题 F: 【递归入门】走迷宫
这题好难呀,留着以后填坑叭orz(逃~
BFS通常用队列来实现
在n*m矩阵中找 1“块 ”的个数
6 7 0 1 1 1 0 0 1 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 1 1 0 1 1 1 0 1 0 0 1 1 1 1 0 0 0
4
队列真好用
#define _CRT_SECURE_NO_WARNINGS 1 #include <cstdio> #include<queue> using namespace std; const int maxn = 100; int matrix[maxn][maxn]; int inq[maxn][maxn] = { 0 };//表示inq[i][j]有没有被访问过 int n, m;//矩阵有n行m列 int X[4] = { 0,0,1,-1 };//增量数组 int Y[4] = { 1,-1,0,0 }; struct node { int x; int y; }Node; int judge(int x, int y)//判断坐标(x,y)是否需要访问,x表示行,y表示列 { if (x < 0 || y < 0 || x >= n || y >= m) return 0;//如果越界,不需要访问 if (inq[x][y] == 1 || matrix[x][y] == 0) return 0; //如果为0或者已经被访问过,不需要访问 return 1;//不然就可以访问 } void BFS(int x, int y) { Node.x = x; Node.y = y; queue < node>q;//定义队列 q.push(Node); inq[x][y] = 1;//!!!注意这个不能忘记,每次push后就记录Inq为1 while (!q.empty()) { node top = q.front(); q.pop();//将队首元素出队 for (int i = 0; i < 4; i++) { int newX = top.x + X[i]; int newY = top.y + Y[i]; if (judge(newX, newY)) { Node.x = newX; Node.y = newY; q.push(Node); inq[newX][newY] = 1;//表示已经访问过 } } } } int main() { scanf("%d %d",&n,&m); int ans = 0; for (int x = 0; x < n; x++) for (int y = 0; y < m; y++) { scanf("%d",&matrix[x][y]); } for(int x=0;x<n;x++) for (int y = 0; y < m; y++) { if (matrix[x][y] == 1 && inq[x][y] == 0) { ans++; BFS(x, y); } } printf("%d\n",ans); return 0; }
走迷宫
5 5 ..... .*.*. .*S*. .***. ...T* 2 2 4 3
11
#define _CRT_SECURE_NO_WARNINGS 1 #include <cstdio> #include<cstring> #include<queue> using namespace std; const int maxn = 100; char maze[maxn][maxn]; int inq[maxn][maxn] = { 0 };//表示inq[i][j]有没有被访问过 int n, m;//矩阵有n行m列 int X[4] = { 0,0,1,-1 };//增量数组 int Y[4] = { 1,-1,0,0 }; struct node { int x; int y; int step; }S,T,Node; int judge(int x, int y)//判断坐标(x,y)是否需要访问,x表示行,y表示列 { if (x < 0 || y < 0 || x >= n || y >= m) return 0;//如果越界,不需要访问 if (inq[x][y] == 1 || maze[x][y] == '*') return 0; //如果为0或者已经被访问过,不需要访问 return 1;//不然就可以访问 } int BFS() { queue < node> q;//定义队列 q.push(S); inq[S.x][S.y] = 1;//!!!注意这个不能忘记,每次push后就记录Inq为1 while (!q.empty()) { node top = q.front(); q.pop();//将队首元素出队 if (top.x == T.x && top.y == T.y) return top.step; for (int i = 0; i < 4; i++) { int newX = top.x + X[i]; int newY = top.y + Y[i]; if (judge(newX, newY)) { Node.x = newX; Node.y = newY; Node.step = top.step + 1; q.push(Node); inq[newX][newY] = 1;//表示已经访问过 } } } return -1;//无法到达终点 } int main() { scanf("%d %d",&n,&m); for (int x = 0; x < n; x++) { getchar();//过滤掉换行符 for (int y = 0; y < m; y++) { maze[x][y] = getchar(); } maze[x][m] = '\0';//最后补上换行符 } scanf("%d %d %d %d",&S.x,&S.y,&T.x,&T.y); S.step = 0; printf("%d", BFS()); return 0; }
需要注意:
1、设置inq数组的含义是节点是否入队,而不是节点被访问
2、queue中的元素只是原来元素的副本,对队列中副本的修改并不会影响原来的元素
一开始我是这样想的:
#define _CRT_SECURE_NO_WARNINGS 1 #include <cstdio> #include<cstring> #include<queue> //!!这题注意所有做过的点都可以再走一遍,所以不需要判断重复,要根据什么来判断有没有走完呢,到A最少需要8步,走完8步也说明一定可以到达A,只要判断level是否等于8就可以了。 using namespace std; char matrix[8][8]; char matrix1[8][8];//matrix的替补 int inq[8][8] = { 0 };//表示inq[i][j]有没有进入队列 int X[9] = { 0,0,0,1,-1,1,-1,1,-1 };//增量数组 int Y[9] = { 0,1,-1,0,0,-1,1,1,-1 };//注意也可以选择不走路 struct node { int x; int y; }S,T,Node; int level; void change1() { for (int i = 7; i >= 0; i--) { for (int j = 0; j < 8; j++) { matrix1[i][j] = matrix[i][j]; } } for (int i = 7; i >= 0; i--) { for (int j = 0; j < 8; j++) { matrix1[i][j] = matrix1[i - 1][j]; } } matrix1[1][7] = '.'; for (int j = 0; j < 7; j++) matrix1[0][j] = '.'; matrix1[0][7] = 'A'; } int judge(int x, int y)//判断坐标(x,y)是否可以入队,x表示行,y表示列 { if (x < 0 || y < 0 || x >= 8 || y >= 8) return 0;//如果越界,不再入队 if ( matrix[x][y] == 'S') return 0; //如果为石头.不再入队 change1(); if (matrix1[x][y] == 'S') return 0;//如果下一步走过去之后为石头 return 1;//不然就可以访问 } void change() { for (int i = 7; i >= 0; i--) { for (int j = 0; j < 8; j++) { matrix[i][j] = matrix[i - 1][j]; } } matrix[1][7] = '.'; for (int j = 0; j < 7; j++) matrix[0][j] = '.'; matrix[0][7] = 'A'; } int BFS() { queue < node> q;//定义队列 q.push(S); level = 0; while (!q.empty()) { node top = q.front(); q.pop();//将队首元素出队 for (int i = 0; i < 9; i++) { int newX = top.x + X[i]; int newY = top.y + Y[i]; if (judge(newX, newY))//如果可以入队 { level++; //printf("%c:",matrix[newX][newY]); //printf("(%d,%d)入队了,level=%d\n", newX, newY, level); Node.x = newX; Node.y = newY; if (level >= 8 || (Node.x == 0 && Node.y == 7)) return 1;//表示可以到达 q.push(Node); } } change(); } return 0;//无法到达终点 } int main() { int n; scanf("%d",&n); int i; for (i = 1; i <= n; i++) { for (int x = 0; x < 8; x++) { getchar();//过滤掉换行符 for (int y = 0; y < 8; y++) { matrix[x][y] = getchar(); } matrix[x][8] = '\0';//最后补上换行符 } S.x = 7, S.y = 0;//左下角方格,起点 T.x = 0, T.y = 7;//右上角方格,终点 if (BFS()) { printf("Case #%d: Yes\n",i); } else { printf("Case #%d: No\n", i); } getchar();//过滤换行符 } return 0; }
但是上面这个代码总是显示错误
#define _CRT_SECURE_NO_WARNINGS 1 #include <cstdio> #include<cstring> #include<queue> //!!这题注意所有做过的点都可以再走一遍,所以不需要判断重复,要根据什么来判断有没有走完呢,到A最少需要8步,走完8步也说明一定可以到达A,只要判断level是否等于8就可以了。 using namespace std; char matrix[8][8]; int inq[8][8] = { 0 };//表示inq[i][j]有没有进入队列 int dir[9][2] = { {0,1},{0,-1},{1,0},{-1,0},{1,1},{-1,-1},{1,-1},{-1,1},{0,0} };//方向向量 struct node { int x,y;//坐标 int step; }S; int sx, sy, ex, ey; node now, nex;//定义两个状态,当前状态和下一个状态 int judge(int x, int y)//判断坐标(x,y)是否可以入队,x表示行,y表示列 { if (x < 0 || y < 0 || x >= 8 || y >= 8) return 0;//如果越界,不再入队 if (matrix[x][y] == 'S') return 0; //如果为石头.不再入队 return 1;//不然就可以访问 } void change()//落石 { for (int i = 7; i >= 0; i--) for (int j = 7; j >= 0; j--) if (matrix[i][j] == 'S') { matrix[i][j] = '.'; if (i <= 6) matrix[i + 1][j] = 'S'; } } void BFS(int c) { int flag = 0; queue < node> q;//定义队列 S.x = sx,S.y = sy, S.step = 0; q.push(S); int level = 0; while (!q.empty()) { now = q.front(); if (now.step != level) { change(); level = now.step; } if (matrix[now.x][now.y] == 'S') { q.pop(); continue; } if (now.step == 8) { flag = 1; break; } q.pop(); for (int i = 0; i < 9; i++) { if (judge(now.x + dir[i][0], now.y + dir[i][1]) == 1) { nex.x = now.x + dir[i][0]; nex.y = now.y + dir[i][1]; nex.step = now.step + 1; q.push(nex); } } } if (flag == 0) printf("Case #%d: No\n", c); else printf("Case #%d: Yes\n", c); } int main() { int n; scanf("%d",&n); int i; for (i = 1; i <= n; i++) { for (int x = 0; x < 8; x++) { scanf("%s",matrix[x]); } for (int x = 0; x < 8; x++) { for (int y = 0; y < 8; y++) { if (matrix[x][y] == 'U') { sx = x, sy = y;//左下角方格,起点 } if (matrix[x][y] == 'A') { ex = x, ey = y;//右上角方格,终点 } } } BFS(i); } return 0; }
这个代码是正确的
改正的代码如下:不得不说bfs的思想很有趣,step==8的判断大大缩短了时间,并且在代码里没有直接判断下一步可不可以走,而是先做一个基本的判断(下标不越界就可以了),在走到是S的坐标时,直接跳过continue,这样就不用每次都要算change1(),导致超时
#define _CRT_SECURE_NO_WARNINGS 1 #include <cstdio> #include<cstring> #include<queue> //!!这题注意所有做过的点都可以再走一遍,所以不需要判断重复,要根据什么来判断有没有走完呢,到A最少需要8步,走完8步也说明一定可以到达A,只要判断step是否等于8就可以了。 using namespace std; char matrix[8][8]; char matrix1[8][8];//matrix的替补 int inq[8][8] = { 0 };//表示inq[i][j]有没有进入队列 int X[9] = { 0,0,0,1,-1,1,-1,1,-1 };//增量数组 int Y[9] = { 0,1,-1,0,0,-1,1,1,-1 };//注意也可以选择不走路 struct node { int x; int y; int step; }S, T, now,nex; int level; int judge(int x, int y)//判断坐标(x,y)是否可以入队,x表示行,y表示列 { if (x < 0 || y < 0 || x >= 8 || y >= 8) return 0;//如果越界,不再入队 if (matrix[x][y] == 'S') return 0; //如果为石头.不再入队 return 1;//不然就可以访问 } void change() { for (int i = 7; i >= 0; i--) { for (int j = 0; j < 8; j++) { matrix[i][j] = matrix[i - 1][j]; } } matrix[1][7] = '.'; for (int j = 0; j < 7; j++) matrix[0][j] = '.'; matrix[0][7] = 'A'; } void BFS(int c) { int flag = 0; queue < node> q;//定义队列 S.step = 0; q.push(S); level = 0; while (!q.empty()) { node now = q.front(); if (now.step != level) { change();//注意并不是时刻要change,只在一层遍历完后才change level = now.step; } if (matrix[now.x][now.y] == 'S')//在这里判断如果是S,那么不需要直接判断下一个点就好了 { q.pop(); continue; } if (now.step == 8) { flag = 1; break; } q.pop(); for (int i = 0; i < 9; i++) { int newX = now.x + X[i]; int newY = now.y + Y[i]; if (judge(newX, newY))//如果可以入队 { nex.x = newX; nex.y = newY; nex.step = now.step+1; q.push(nex); } } } if (flag == 0) printf("Case #%d: No\n", c); else printf("Case #%d: Yes\n", c); } int main() { int n; scanf("%d", &n); int i; for (i = 1; i <= n; i++) { for (int x = 0; x < 8; x++) { scanf("%s", matrix[x]); } for (int x = 0; x < 8; x++) { for (int y = 0; y < 8; y++) { if (matrix[x][y] == 'U') { S.x = x, S.y = y;//左下角方格,起点 } if (matrix[x][y] == 'A') { T.x = x, T.y = y;//右上角方格,终点 } } } BFS(i); } return 0; }
问题 C: 【宽搜入门】8数码难题
大神的分析帖:
https://blog.csdn.net/u012283461/article/details/79078653
因为BFS缓了好几天orz,我决定满满把这些题目和题解看了,目前就先这样叭