DFS深度优先搜索
部分和问题:
给定整数a1 ,a2 ,… an,判断是否可以从中选出若干数,使得他们的和恰好为k。若存在输出true,否则输出false.
int a[MAX_N]; //a[0]~a[n-1]
int n,k;
bool dfs(int i,int sum) //表示已经从前i个数做出选择,当前所选数的和为sum.i=0时表示还未开始选择
{
if(i==n) return sum==k; //i=n时,对前n个数都做出了选择,选择完毕,应该输出结果
//dfs(i,sum)表示已经对a[i-1]及之前的数进行了选择。
//当前需要选择是否加入a[i]
if(dfs(i+1,sum)) return true;
if(dfs(i+1,sum+a[i])) return true;
return false; //如果选或不选a[i]都不能,则返回false
}
void solve()
{
if(dfs(0,0)) printf("true");
else printf("false");
}
BFS宽度优先搜索
使用队列实现,对于图中的点,第一次遍历到该点时,就找到了该点的最短路径。
以CCF-最优配餐为例
#include<bits/stdc++.h>
using namespace std;
//在图中不需要记录商家的位置,直接将商家的位置压入队列中,就不用在grid[][]中区别商家,用户,障碍
class Point{
public:
int x,y,cost; //该点坐标以及 所有商家到该点的最短距离
Point(int xx=0,int yy=0,int ccost=0):x(xx),y(yy),cost(ccost) {}
};
int dx[]={1,0,-1,0};
int dy[]={0,1,0,-1};
int grid[1002][1002]; //grid中,-1代表障碍,0代表可走的路,>0表示客户的数量
//int shortlen[1002][1002]; shortlen中可能有很多空白,所以在point中加cost代替
bool shortfind[1002][1002]; //记录该点是否已找到最短路径
int main()
{
memset(grid,0,sizeof(grid));
memset(shortfind,0,sizeof(shortfind));
int n,m,k,d;
scanf("%d %d %d %d",&n,&m,&k,&d);
queue<Point> Q;
for(int i=0;i<m;i++)
{
int x,y;
scanf("%d %d",&x,&y);
Point shopp(x,y,0);
Q.push(shopp);
shortfind[x][y]=true;
}
for(int i=0;i<k;i++)
{
int x,y,num;
scanf("%d %d %d",&x,&y,&num);
grid[x][y]+=num;
}
for(int i=0;i<d;i++)
{
int x,y;
scanf("%d %d",&x,&y);
grid[x][y]=-1;
}
long long res=0; //long long保存结果--------因为数字很大
while(!Q.empty())
{
Point tempP=Q.front();
Q.pop();
int x=tempP.x,y=tempP.y,c=tempP.cost;
if(grid[x][y]>0)
res+=(long long)grid[x][y]*c; //在出队时对出队节点处理,这样保证每个节点都会处理到
int xx,yy,cc;
for(int i=0;i<4;i++) //四个方向
{
xx=tempP.x+dx[i];
yy=tempP.y+dy[i];
cc=tempP.cost+1;
//不能超过边界,不能走到障碍物,不能走已经找到最短路的点
if(xx>0&&xx<=n&&yy>0&&yy<=n&&!shortfind[xx][yy]&&grid[xx][yy]>=0)
{
Q.push(Point(xx,yy,cc));
shortfind[xx][yy]=true;
}
}
}
printf("%lld",res);
}
-
对于Grid数组保存的是图的信息,需要体现障碍(-1),客户订餐数(>0数),普通道路(0)的信息。
-
对于源点,一开始输入时就将其放入队列中,所以不用在grid图中标记
-
还需要shortfind数组记录该点是否找到最短路径(bfs第一次遍历到该点时得到的就是最短路径
-
对于该题,认证读题和示例后发现最左下角的点表示为(1,1),所以整个图的范围是(1,1)~(n,n)在判断点是否在图中时需要使用边界来判定。
-
该题和普通的bfs不同的是有多个源点,在起始时一次把所有源点放入队列中,之后bfs求出来的最短距离就是所有源点距离最近的源点的最短距离。(虽然源点放入队列中是按顺序的,但在bfs中是从源点一层一层求的(看作以源点为圆心向外发散,所以最先波及到的点直接就确定了所有源点总的最短距离。
拓扑排序
拓扑排序的定义
在图论中,拓扑排序(Topological Sorting)是一个有向无环图(DAG, Directed Acyclic Graph)的所有顶点的线性序列。且该序列必须满足下面两个条件:
1.每个顶点出现且只出现一次。
2.若存在一条从顶点 A 到顶点 B 的路径,那么在序列中顶点 A 出现在顶点 B 的前面。
有向无环图(DAG)才有拓扑排序,非DAG图没有拓扑排序一说。
实现方法:
- 入度删边法:
每次选择当前入度为0的节点,将该节点和其出边删除。
这样循环选择,直到所有节点都被删除或者存在节点但是没有入度为零,对于后者表示图中有环。
实现时使用indegree[]
和delete[]
记录节点入度和被删除节点,对于当前入度为0的节点,可以放入queue
中
(以此类推同样可以使用出度(为0)删边法,这样求得是拓扑排序的逆序,放入stack
中就可正序输出)
-
dfs搜索
(相当于出度为0选择,对于一个节点,如果所有的出边都dfs访问完,则将该节点入stack。(但这种方法不能检查出环路,所以之前可以用普通的dfs做一遍检测环路,若有环路,则没有拓扑排序)
https://zhuanlan.zhihu.com/p/34871092
https://blog.youkuaiyun.com/lalor/article/details/7392793
https://blog.youkuaiyun.com/tianshuai1111/article/details/7041571
伪代码:
DFS-IMPROVE(v,visited,stack) //dfs
visited[v] = true
for i equal to every vertex adjacent to v
if visited[i] == false
DFS-IMPROVE(i,visited,stack)
end
stack.push(v) //出边全部遍历完,将该节点入stack
TOPOLOGICAL-SORTING-DFS(g) //dfs拓扑排序主程序
let visited be new Array
let result be new Array
let stack be new Stack
for v equal to every vertex in g //对于非连通图也可求拓扑序
if visited[v] == false
DFS-IMPROVE(v,visited,stack)
end
while stack.empty() == false //将stack倒序输出
result.append(stack.top())
stack.pop()
end
return result
强连通分量