算法基础课之搜索也图论
DFS
#include<bits/stdc++.h>
using namespace std;
const int N = 10;
int path[N]; // 存状态
int state[N]; // 数字i是否被用过
int n;
// u 指第一个开始填的位置,从1开始
void dfs(int u)
{
// 如果填的位数大于n,则输出
if(u>n)
{
for(int i = 1;i<=n;i++)
{
cout<<path[i]<<' ';
}
cout<<endl;
}
// 填数字
for(int i=1;i<=n;i++)
{
// 如果数字未被填入
if(!state[i])
{
path[u]=i; // 填入数字
state[i]=1; // 状态改变
dfs(u+1); // 填下一位
state[i]=0; // 回溯
}
}
}
int main()
{
cin>>n;
dfs(1);
return 0;
}
#include<bits/stdc++.h>
using namespace std;
const int N = 11;
char q[N][N];
bool cor[N*2],dg[N*2],udg[N*2];
int n;
void dfs(int r)
{
if(r==n)
{
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
cout<<q[i][j];
}
cout<<endl;
}
cout<<endl;
return;
}
for(int i=0;i<n;i++)
{
if(!cor[i]&&!dg[i+r]&&!udg[n-i+r])
{
q[r][i]='Q';
cor[i]=dg[i+r]=udg[n-i+r]=1;
dfs(r+1);
cor[i]=dg[i+r]=udg[n-i+r]=0;
q[r][i]='.';
}
}
}
int main()
{
cin>>n;
//初始化棋盘状态
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
q[i][j]='.';
}
}
dfs(0);
return 0;
}
BFS
#include<bits/stdc++.h>
using namespace std;
const int N = 110;
typedef pair<int,int>PII;
int g[N][N];
int d[N][N];
PII q[N*N];
int n,m;
int bfs()
{
int hh=0,tt=0;
q[0]={0,0};
memset(d,-1,sizeof d);
d[0][0]=0;
while(hh<=tt)
{
PII t=q[tt++];
int x=
}
}
int main()
{
cin>>n>>m;
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
cin>>g[i][j];
}
}
cout<<bfs()<<endl;
return 0;
}
#include<bits/stdc++.h>
using namespace std;
int bfs(string start)
{
// 定义最终状态
string end = "12345678x";
// 定义队列和dist数组(用哈希表存储)
queue<string>q;
unordered_map<string, int>d;
// 初始化队列和数组
q.push(start);
d[start]=0;
//定义4个方向
int dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};
// 当队列不空时循环
while(q.size())
{
// 取队首元素
auto t = q.front();
q.pop();
int distance = d[t];
if(t == end) return distance;
//枚举4个方向
int k = t.find('x');
int x = k/3, y = k%3;
for(int i=0;i<4;i++)
{
int a = x+dx[i], b = y+dy[i];
if(a>=0&&a<3&&b>=0&&b<3)
{
// 转移x
swap(t[k],t[a*3+b]);
// 保证第一次遍历,避免重复遍历状态,从而保证最少
if(!d.count(t))
{
d[t]=distance+1; // 距离+1
q.push(t); // 状态入队
}
swap(t[k],t[a*3+b]); // 恢复状态
}
}
}
// 否则无法到达状态,输出-1
return -1;
}
int main()
{
string start;
for(int i=0;i<9;i++)
{
char c;
cin>>c;
start+=c;
}
cout<<bfs(start)<<endl;
return 0;
}
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100010;
int e[N], ne[N], idx;//邻接表存储图
int h[N];
int q[N], hh = 0, tt = -1;//队列保存入度为0的点,也就是能够输出的点,
int n, m;//保存图的点数和边数
int d[N];保存各个点的入度
void add(int a, int b){
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void topsort(){
for(int i = 1; i <= n; i++){//遍历一遍顶点的入度。
if(d[i] == 0)//如果入度为 0, 则可以入队列
q[++tt] = i;
}
while(tt >= hh){//循环处理队列中点的
int a = q[hh++];
for(int i = h[a]; i != -1; i = ne[i]){//循环删除 a 发出的边
int b = e[i];//a 有一条边指向b
d[b]--;//删除边后,b的入度减1
if(d[b] == 0)//如果b的入度减为 0,则 b 可以输出,入队列
q[++tt] = b;
}
}
if(tt == n - 1){//如果队列中的点的个数与图中点的个数相同,则可以进行拓扑排序
for(int i = 0; i < n; i++){//队列中保存了所有入度为0的点,依次输出
cout << q[i] << " ";
}
}
else//如果队列中的点的个数与图中点的个数不相同,则可以进行拓扑排序
cout << -1;//输出-1,代表错误
}
int main(){
cin >> n >> m;//保存点的个数和边的个数
memset(h, -1, sizeof h);//初始化邻接矩阵
while (m -- ){//依次读入边
int a, b;
cin >> a >> b;
d[b]++;//顶点b的入度+1
add(a, b);//添加到邻接矩阵
}
topsort();//进行拓扑排序
return 0;
}
#include<bits/stdc++.h>
using namespace std;
const int N = 510;
int g[N][N]; //稠密图用领接矩阵存
int dist[N]; //用于存储每个点到第一个点的距离
bool st[N];//用于确定每个点是否在最短路径中
int n,m;
int Dijkstra()
{
memset(dist,0x3f3f3f3f,sizeof(dist));//初始化距离为无穷大
dist[1]=0;//第一个点从1开始存储,自身到自身的距离为0
for(int i=0;i<n;i++)
{
int t=-1;
for(int j=1;j<=n;j++)
{
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;
return dist[n];
}
int main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>m;
memset(g,0x3f3f3f3f,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;
}
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int>PII;
const int N = 1000010;
const int M = 1000010;
int n,m;
//稀疏图用邻接表来存
int h[N];//存储第i个结点的头结点的下标
int e[M];//存结点i的值,有向边的终点
int ne[M];//存储结点i的下一个结点的下标
int idx;//存储当前可用的结点下标
int w[M];//存储权值
bool st[N];//记录该点是否在最短路径中
int dist[N];//存储最短路径
//memset(h,-1,sizeof h);
//C++中,全局域只能声明、初始化变量; 不能用于赋值、运算、调用函数
//插入一条有向边a --> b,权值为c
void add(int a,int b, int c)
{
w[idx]=c;
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
//小根堆优化版的Dijkstra算法
int Dijkstra()
{
//先初始化
memset(dist,0x3f3f3f3f,sizeof(dist));
dist[1]=0;
//定义小根堆heap
priority_queue<PII,vector<PII>,greater<PII>>heap;
//插入第一个点
heap.push({0, 1});
while(heap.size())
{
PII k = heap.top();//取栈顶元素,最小值
heap.pop();//出栈
int ver = k.second, distance=k.first;
if(st[ver]) continue;//如果终点元素在最短路径中,则不必进行下面操作
st[ver]=true;
//从终点开始循环
for(int i=h[ver];i!=-1;i=ne[i])
{
int j = e[i];//i只是个下标,e[i]中存的是i这个下标对应的值
if(dist[j]>distance+w[i])
{
dist[j]=distance+w[i];
heap.push({dist[j],j});
}
}
}
if(dist[n]==0x3f3f3f3f) return -1;
else return dist[n];
}
int main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
//第一件事就是初始化头结点
memset(h,-1,sizeof h);
cin>>n>>m;
while(m--)
{
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
}
cout<<Dijkstra()<<endl;
return 0;
}
#include<bits/stdc++.h>
using namespace std;
const int N = 510;
const int M = 10010;
int n,m,k;
int dist[N];
int last[N];
//定义结构体
struct Edge{
int a,b,c;
}edges[M];
//bellman_ford函数
void bellman_ford()
{
//第一步,初始化dist数组为无穷大
memset(dist,0x3f,sizeof dist);
dist[1]=0;
for(int i=0;i<k;i++){
memcpy(last,dist,sizeof dist);
//循环m条边
for(int j=0;j<m;j++)
{
//存储,转换
Edge e = edges[j];
dist[e.b]=min(dist[e.b],last[e.a]+e.c);
}
}
}
int main()
{
cin>>n>>m>>k;
for(int i=0;i<m;i++)
{
int a,b,c;
cin>>a>>b>>c;
edges[i]={a,b,c};
}
bellman_ford();
if(dist[n]>0x3f3f3f3f/2) cout<<"impossible"<<endl;
else cout<<dist[n]<<endl;
return 0;
}
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+10;
typedef pair<int ,int>PII;
int h[N];
int e[N];
int ne[N];
int w[N];
int idx,n,m;
int dist[N];
bool st[N];
void add(int a,int b,int c)
{
w[idx]=c;
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
int spfa()
{
//第一步初始化dist
memset(dist,0x3f,sizeof dist);
dist[1]=0;
//第二步,定义队列
queue<PII>q;
q.push({0,1});
st[1]=true;
//第三步循环
while(q.size())
{
PII p= q.front();
q.pop();
int t =p.second;
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];
if(!st[j])
{
st[j]=true;
q.push({dist[j],j});
}
}
}
}
return dist[n];
}
int main()
{
memset(h,-1,sizeof h);
cin>>n>>m;
for(int i=0;i<m;i++)
{
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
}
int res = spfa();
if(res==0x3f3f3f3f) cout<<"impossible"<<endl;
else cout<<res<<endl;
return 0;
}
/*
dijkstra 算法 用于求权值为非负的最短路径
朴素的dijkstra算法 o(n^2)
堆优化的dijkstra算法 o(mlogn)
bellman_ford算法用于有负权回路,且存在最多路径k的限制的最短路径 o(nm)
spfa算法可用于边权值为负,但不能存在负权回路的最短路径 一般O(m),最坏O(nm)
*/
/*
Dijkstra算法中的st数组保存的是当前确定了到源点距离最小的点,且一旦确定了最小那么就不可逆了(不可标记为true后改变为false);SPFA算法中的st数组仅仅只是表示的当前发生过更新的点,且spfa中的st数组可逆(可以在标记为true之后又标记为false)。顺带一提的是BFS中的st数组记录的是当前已经被遍历过的点
*/
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
typedef pair<int,int>PII;//到源点的距离,下标号
int h[N];
int e[N];
int w[N];
int ne[N];
int idx=0;
int dist[N];
int cnt[N];
bool st[N];
int n,m;
void add(int a,int b,int c)
{
w[idx]=c;
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
bool spfa(){
//定义队列 q
queue<PII>q;
memset(dist,0x3f,sizeof dist);
dist[1]=0;
//所有点都入队
for(int i=1;i<=n;i++)
{
st[i]=true;
q.push({dist[i],i});
}
while(q.size())
{
PII p=q.front();
q.pop();
int t=p.second;
//从队列取出之后该节点st被标记为false,代表之后该节点如果发生更新可再次入队
st[t]=false;
//从t开始循环
for(int i=h[t];i!=-1;i=ne[i])
{
//取新点j
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;
//若j为更新,则更新,入队
if(!st[j])
{
st[j]=true;
q.push({dist[j],j});
}
}
}
}
return false;
}
int main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>m;
memset(h,-1,sizeof h);
while(m--)
{
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
}
if(spfa()) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
return 0;
}
#include<bits/stdc++.h>
using namespace std;
const int N = 210;
const int INF = 1e9;
int d[N][N];
int n,m,k;
void floyd()
{
for(int k=1;k<=n;k++)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
}
}
}
}
int main()
{
cin>>n>>m>>k;
//初始化d[i][j]数组为无穷大
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(i==j) d[i][j]=0;
else d[i][j]=INF;
}
}
while(m--)
{
int x,y,z;
cin>>x>>y>>z;
d[x][y]=min(d[x][y],z);//考虑有重边的情况
}
floyd();
while(k--)
{
int x,y;
cin>>x>>y;
if(d[x][y]>INF/2) cout<<"impossible"<<endl;
else cout<<d[x][y]<<endl;
}
return 0;
}
#include<bits/stdc++.h>
using namespace std;
const int N = 510,INF = 0x3f3f3f3f;
int g[N][N];//存无向边
int dist[N];//存i点到集合的最短距离
bool st[N];//判断i点是否在最短生成树的集合中
int n,m;
int prim()
{
//第一步初始化dist数组为无穷大
memset(dist,0x3f,sizeof dist);
//将第一个点的距离设为0
dist[1]=0;
int res=0;//res用于记录最小生成树的长度
//循环点次,用于将每个点都加在最小生成树中
for(int i=0;i<n;i++)
{
int t=-1;//用于记录新点
for(int j=1;j<=n;j++)
{
if(!st[j]&&(t==-1||dist[t]>dist[j]))
{
t=j;
}
}
if(dist[t]==INF) return INF;//直接不连通,没有最小生成树,直接返回不必做下面的操作
//加入最小生成树路径和中
res+=dist[t];
//点加入集合中
st[t]=true;
//更新各点到新集合的最短距离,dist和g数组都是从1开始存储
for(int j=1;j<=n;j++)
{
dist[j]=min(dist[j],g[t][j]);
}
}
return res;
}
int main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
//第一步,初始化无向图为无穷大
memset(g,0x3f,sizeof g);
//第一步输入点数和边数
cin>>n>>m;
//输入m条边
while(m--)
{
int a,b,c;
cin>>a>>b>>c;
//因为是无向图且可能有重边
g[a][b]=g[b][a]=min(g[a][b],c);
}
int t= prim();
if(t==INF) cout<<"impossible"<<endl;
else cout<<t<<endl;
return 0;
}
#include<bits/stdc++.h>
using namespace std;
//因为是无向图,所以是有M
const int N = 100010,M=200010,INF=0x3f3f3f3f;
int n,m;
int p[N];//存前项节点
//定义数据结构存储边
struct Edge{
int a,b,w;
bool operator< (const Edge &W)const
{
return w<W.w;
}
}edges[M];
int find(int x)
{
if(p[x]!=x) p[x]=find(p[x]);//这一步递归了
return p[x];
}
int kruskal()
{
//第一步排序
sort(edges,edges+m);
//第二步初始化并查集
for(int i=1;i<=n;i++)
{
p[i]=i;
}
int res=0,cnt=0;
for(int i=0;i<m;i++)
{
int a=edges[i].a,b=edges[i].b,w=edges[i].w;
a=find(a),b=find(b);
if(a!=b){
p[a]=b;
res+=w;
cnt++;
}
}
if(cnt<n-1) return INF;
else return res;
}
int main()
{
cin>>n>>m;
for(int i=0;i<m;i++)
{
int a,b,w;
cin>>a>>b>>w;
edges[i]={a,b,w};
}
int t=kruskal();
if(t==INF) cout<<"impossible"<<endl;
else cout<<t<<endl;
return 0;
}