目录
DFS
1.排列数字
对于每个节点,只能选择没有用过的数,因此需要一个 s t [ ] st[] st[] 数组,当我们枚举到底层的时候,需要清空状态
void dfs(int u){
if( u == n+1){
for(int i = 1;i<=n;i++)
cout<<ans[i]<<" ";
cout<<endl;
return;
}
for(int i=1;i<=n;i++){
if(!st[i]){
ans[u] = i;
st[i] = 1;
dfs(u+1);
st[i] = 0;
}
}
}
2.n-皇后
搜索行然后根据 函数式进行操作
BFS
1. 走迷宫
很典型的 b f s bfs bfs问题,需要两个方向数组,然后我们对每一个走过的点进行标记即可
2.八数码
虽然这个题看着不像是 b f s bfs bfs,但是我们对于每一个状态进行 h a s h hash hash那么还是可以使用 b f s bfs bfs的
树与图的深度优先遍历
1.树的重心
树的重心 , 对于树上的每一个点,计算其子树中最大的子树节点,这个值最小的点就是这棵树的重心
int dfs(int u){
int res = 0 ;//删除当前点,连通块中的最大值
st[u] = 1;
int sum = 1 ;//以u为根的 子树
for(auto x : g[u]){
if(!st[x]){
int t = dfs(x);
res = max(res,t);
sum+=t;
}
}
res = max(res,n-sum);
ans = min(ans,res);
return sum;
}
2. 图中点的层次
利用 b f s bfs bfs寻找 1 − n 1-n 1−n的最短路径,但是不怎么怎么 W A WA WA 了
int d[N];
int n,m;
vector<int> g[N];
void bfs(){
queue<int> q;
d[1] = 0 ;
q.push(1);
while(!q.empty()){
auto t = q.front();
q.pop();
for(auto x : g[t]){
if(d[x] == -1){
d[x] = d[t]+1;
q.push(x);
}
}
}
拓扑排序
1.拓扑排序
无向图 : 记录每个点的入度,然后将所有入度为 0 0 0 的点放进队列中,每次取出队头,然后令其领边的入度 − − -- −−,反复如此操作
bool topsort()
{
int num =0;
queue<int> q;
for(int i=1;i<=n;i++)
{
if(!d[i])
q.push(i);
}
while(!q.empty())
{
num++;
int t=q.front();
ans[dx++] = t;
q.pop();
for(int i=h[t];i!=-1;i=ne[i])
{
int j =e[i];
d[j] --;
if(d[j] ==0)
q.push(j);
}
}
if(num ==n)
return true;
else
return false;
}
Dijkstra
1.Dijkstra求最短路 I
这里使用的 邻接矩阵 进行建立的图,怪不得这个题是简单
普通的 D i j Dij Dij算法,流程如下 :
- n n n次迭代
- 找到最小的并且没有使用的点
- 利用这个点进行更新
int g[N][N]; //为稠密阵所以用邻接矩阵存储
int dist[N]; //用于记录每一个点距离第一个点的距离
bool st[N]; //用于记录该点的最短距离是否已经确定
int n,m;
int Dijkstra()
{
memset(dist, 0x3f,sizeof dist); //初始化距离 0x3f代表无限大
dist[1]=0; //第一个点到自身的距离为0
for(int i=0;i<n;i++) //有n个点所以要进行n次 迭代
{
int t=-1; //t存储当前访问的点
for(int j=1;j<=n;j++) //这里的j代表的是从1号点开始
if(!st[j]&&(t==-1||dist[t]>dist[j]))
t=j;
st[t]=true;
for(int j=1;j<=n;j++) //依次更新每个点所到相邻的点路径值
dist[j]=min(dist[j],dist[t]+g[t][j]);
}
if(dist[n]==0x3f3f3f3f) return -1; //如果第n个点路径为无穷大即不存在最低路径
return dist[n];
}
int main()
{
cin>>n>>m;
memset(g,0x3f,sizeof g); //初始化图 因为是求最短路径
//所以每个点初始为无限大
while(m--)
{
int x,y,z;
cin>>x>>y>>z;
g[x][y]=min(g[x][y],z); //如果发生重边的情况则保留最短的一条边
}
cout<<Dijkstra()<<endl;
return 0;
}
2.Dijkstra求最短路 II
超级简单.就是使用一个小根堆做到 l o g log log级别的维护集合外的最小的点
// Problem: Dijkstra求最短路 II
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/852/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <iostream>
#include <vector>
#include <map>
#include <cstring>
#include <queue>
#include <math.h>
#include <set>
#include <stack>
#include <algorithm>
using namespace std;
#define IOS ios::sync_with_stdio(false);
#define CIT cin.tie(0);
#define COT cout.tie(0);
#define ll long long
#define x first
#define y second
#define pb push_back
#define endl '\n'
#define all(x) (x).begin(),x.end()
#define Fup(i,a,b) for(int i=a;i<=b;i++)
#define Fde(i,a,b) for(int i=a;i>=b;i--)
typedef priority_queue<int,vector<int>,greater<int>> Pri_m;
typedef pair<int,int> pii;
typedef vector<int> VI;
map<int,int> mp;
const int N = 2e5+10,INF = 0x3f3f3f3f3f;
const double eps = 1e-5;
int n,m;
int dist[N];
int st[N];
struct node{
int to,val;
};
vector<node> g[N];
void dij(){
memset(dist,0x3f,sizeof dist);
dist[1] = 0 ;
priority_queue<pii,vector<pii>,greater<pii>> heap;
heap.push({0,1});
while(heap.size()){
auto t = heap.top();
heap.pop();
if(st[t.y]) continue;
st[t.y] = 1;
for(auto x : g[t.y]){
if(dist[x.to] > t.x + x.val){
dist[x.to] = t.x + x.val;
heap.push({dist[x.to],x.to});
}
}
}
}
void solve(){
cin>>n>>m;
for(int i=1;i<=m;i++){
int a,b,c;cin>>a>>b>>c;
g[a].pb({b,c});
}
dij();
if(dist[n] == INF) cout<<-1<<endl;
else cout<<dist[n]<<endl;
}
int main(){
//int t;cin>>t;while(t--)
solve();
return 0 ;
}
Bellman-Ford
B e l l m a n − F o r d Bellman-Ford Bellman−Ford基本思路 :
- 迭代 n n n次
- 对于每次迭代,我们对所有边进行松弛操作
for(int i=1;i<=n;i++)//迭代n 次
for(int j=1;j<=n;j++)//枚举每个点
for(auto x : g[j])//对所有边进行松弛操作
dist = min(dist,dist+w);
1.有边数限制的最短路
因为第一层循环的本质是
从
1
1
1号店开始更新不超过
n
n
n条边的最短路,因此我们只需要把
n
n
n改为
k
k
k
另外第一层也可以理解为
d
i
s
t
[
n
]
dist[n]
dist[n]是走过
k
k
k步以及以内的最短路
当然这题有一个非常特别的点,就是需要存放上一层的 d i s t dist dist
#include<iostream>
#include<cstring>
using namespace std;
const int N = 510,M=10010;
struct Edge {//只需要将边遍历一遍,用结构体数组储存即可
int a, b, c;
} edges[M];
int n, m, k;
int dist[N],last[N];
void bellman_ford(){
memset(dist,0x3f,sizeof dist);
dist[1]=0;
for(int i=0;i<k;i++){//保证第k次时,dist[n]是走过k步及以内的最短路。第k+5次时dist[n]的最短路第k+5次时再确定!!!
memcpy(last,dist,sizeof dist);//保留上一次迭代的dist的副本,防止跨层数更新
for(int j=0;j<m;j++){
auto t=edges[j];
dist[t.b]=min(dist[t.b],last[t.a]+t.c);//用已确定最短路的点向外延申,类似于dijkstra
}
}
}
int main() {
cin >> n >> m >> k;
for (int i = 0; i < m; i++) {
int x, y, z;
scanf("%d%d%d", &x, &y, &z);
edges[i] = {x, y, z};
}
bellman_ford();
if(dist[n]>0x3f3f3f3f/2)//0x3f3f3f3f并非真正的无穷大,会随着dist数值更新而受到影响
cout<<"impossible";//输出字符串或数字,不能用三目运算符
else cout<<dist[n];
}
Spfa
每次只把能更新的点放进队列中,一个点可以被放进多次
1.Spfa求最短路
int spfa(){
queue<PII> q;
memset(dist,0x3f,sizeof dist);
dist[1]=0;
q.push({0,1});
st[1]=true;
while(q.size()){
PII p=q.front();
q.pop();
int t=p.se;
st[t]=false;//从队列中取出来之后该节点st被标记为false,代表之后该节点如果发生更新可再次入队
for(int i=h[t];i!=-1;i=ne[i]){
int j=e[i];
if(dist[j]>dist[t]+w[i]){
dist[j]=dist[t]+w[i];
if(!st[j]){//当前已经加入队列的结点,无需再次加入队列,即便发生了更新也只用更新数值即可,重复添加降低效率
st[j]=true;
q.push({dist[j],j});
}
}
}
}
if(dist[n]==0x3f3f3f3f) return -1;
else return dist[n];
}
2.spfa判断负环
对于每次更新的时候我们多记录一个 c n t [ ] cnt[] cnt[]
如果存在当前点被放进队列 n n n 次,那么即存在负环
bool spfa()
{
queue<int> q;
for (int i = 1; i <= n; i ++ )
{
st[i] = true;
q.push(i);
}
while (q.size())
{
int t = q.front();
q.pop();
st[t] = false;
for (int i = h[t]; i != -1; i = ne[i])
{
int j = e[i];
if (dist[j] > dist[t] + w[i])
{
dist[j] = dist[t] + w[i];
cnt[j] = cnt[t] + 1;
if (cnt[j] >= n) return true;
if (!st[j])
{
q.push(j);
st[j] = true;
}
}
}
}
return false;
}
Floyd
1.Floyd求最短路
枚举中间更换点 k k k,然后枚举起点 i i i 到终点 j j j,判断是否可以利用 k k k进行转移
for(int k1 = 1;k1<=n;k1++)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
dist[i][j] = min(dist[i][j],dist[i][k1]+dist[k1][j]);
}
}
}