排列数字
#include <iostream>
using namespace std;
const int N = 10;
int path[N];
bool st[N];
int n;
// 计算u->n的所经过的路径path
void dfs(int u)
{
// 边界条件
if (u == n) //这就是走到头了
{
for (int i = 0; i < n; i++) printf("%d ", path[i]); //直接输出路径
puts(""); //换行
return; //返回空值
}
else //如果没有走到头
{
for (int i = 1; i <= n; i++) //枚举1~n
{
if (!st[i]) //找到那些数字没有用过
{
path[u] = i; //此时u这个节点对应的值为i
st[i] = true; //同时标记st[i]这个点已经访问过了
dfs(u+1); //向下递归,所以u+1
//剩下这里表示,回溯的状态,回来时需要恢复现场
path[u] = 0; //设置u这个节点对应的值为0
st[i] = false; //同时标记st[i]这个点没有访问过
}
}
}
}
int main()
{
scanf("%d", &n); //输入n
dfs(0); //从0节点出发
return 0;
}
n皇后
#include<iostream>
using namespace std;
const int maxn=20;
int col[maxn],gcd[maxn],ugcd[maxn];
char g[maxn][maxn];
int n;
void dfs(int u)
{
if(u==n)
{
for(int i=0; i<n; i++)
puts(g[i]); //puts直接输出g[i],并且自动输出换行
cout << endl;
return ;
}
for(int i=0; i<n; i++)
{
if(!col[i] && !gcd[u+i] && !ugcd[i-u+n])
{
g[u][i]='Q';
col[i]=gcd[u+i]=ugcd[i-u+n]=true;
dfs(u+1);
col[i]=gcd[u+i]=ugcd[i-u+n]=false;
g[u][i]='.';
}
}
}
int main()
{
cin >> n; //读入n
for(int i=0; i<n; i++) //先对g初始化为 .
for(int j=0; j<n; j++)
g[i][j]='.';
dfs(0);
return 0;
}
走迷宫
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
typedef pair<int, int> PII; //用pair来存放每个点的坐标
const int N = 110;
int n, m;
int g[N][N], d[N][N]; //g存放的是每个图,d存放的是(0,0)点到每个点的距离
int bfs()
{
queue<PII> q;
memset(d, -1, sizeof d); //所有的距离初始化为-1,表示该点没有走过
d[0][0] = 0; //初始化(0,0)这个点表示已经走过了
q.push({0, 0}); //将(0,0)放到队列q中
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1}; //上下左右4个方向
while (q.size())
{
auto t = q.front(); //访问队头元素
q.pop();
for (int i = 0; i < 4; i ++ )
{
int x = t.first + dx[i], y = t.second + dy[i];
if (x >= 0 && x < n && y >= 0 && y < m && g[x][y] == 0 && d[x][y] == -1) //g[x][y]==0,表示空地,d[x][y]表示-1,表示该点之前没有走过
{
d[x][y] = d[t.first][t.second] + 1;
q.push({x, y});
}
}
}
return d[n - 1][m - 1];
}
int main()
{
cin >> n >> m; //输入n行,m列
for (int i = 0; i < n; i ++ ) //输入n行m列
for (int j = 0; j < m; j ++ )
cin >> g[i][j]; //输入其地图
cout << bfs() << endl; //直接输出从左上角到右下角的最少移动次数
return 0;
}
八数码
#include <iostream>
#include <algorithm>
#include <queue>
#include <unordered_map>
using namespace std;
int bfs(string start)
{
//定义目标状态
string end = "12345678x";
//定义队列和dist数组
queue<string> q; //q表示对列的一个状态
unordered_map<string, int> d; //d表示前面的装态及对应后面的 距离
//初始化队列和dist数组
q.push(start); //先将start放入到q中
d[start] = 0;
//转移方式
int dx[4] = {1, -1, 0, 0}, dy[4] = {0, 0, 1, -1}; //4个方向
while(q.size())
{
auto t = q.front(); //t表示q.front()
q.pop();
//记录当前状态的距离,如果是最终状态则返回距离
int distance = d[t];
if(t == end) return distance;
//查询x在字符串中的下标,然后转换为在矩阵中的坐标
int k = t.find('x'); //在字符串中找到x所处在的位置
int x = k / 3, y = k % 3; //找到x对应的,第几行第几列
for(int i = 0; i < 4; i++)
{
//求转移后x的坐标
int a = x + dx[i], b = y + dy[i]; //a,b是新的下标
//当前坐标没有越界
if(a >= 0 && a < 3 && b >= 0 && b < 3)
{
//转移x
swap(t[k], t[a * 3 + b]); //k是当前的下标,
//如果当前状态是第一次遍历,记录距离,入队
if(!d.count(t))
{
d[t] = distance + 1;
q.push(t);
}
//还原状态,为下一种转换情况做准备
swap(t[k], t[a * 3 + b]);
}
}
}
//无法转换到目标状态,返回-1
return -1;
}
int main()
{
string c, start;
for(int i = 0; i < 9; i++)
{
cin >> c;
start += c;
}
cout << bfs(start) << endl;
return 0;
}
树的重心
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 1e5 + 10;
const int M = 2 * N;
int h[N];
int e[M]; //e和ne的空间为2*N
int ne[M];
int idx;
int n;
int ans = N; //全局的最大最小值
bool st[N];
void add(int a, int b) //套路
{
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int dfs(int u)
{
int res = 0; //表示将该点删掉,每个连通块的最大值
st[u] = true; //标记该点已经访问过了
int sum = 1; //sum表示当前子树的大小,所以从1开始
//sum表示将该点删掉后,并且包括该点的,总点数
for (int i = h[u]; i != -1; i = ne[i])
{
int j = e[i];
if (!st[j]) //如果该点没有访问过
{
int s = dfs(j); //s表示当前子树的大小
res = max(res, s); //s跟res取max
sum += s;
}
}
res = max(res, n - sum);
ans = min(res, ans);
return sum;
}
int main()
{
memset(h, -1, sizeof h);
cin >> n;
for (int i = 0; i < n - 1; i++)
{
int a, b;
cin >> a >> b;
add(a, b), add(b, a); //无向图
}
dfs(2); //从任意节点出发
cout << ans << endl; //ans表示最大的最小值
return 0;
}
图中点的层次
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 100010;
int n, m;
int h[N], e[N], ne[N], idx; //单向图的e和ne都是N
int d[N];
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
int bfs()
{
memset(d, -1, sizeof d); //d表示从1-每个点的最短距离
queue<int> q;
d[1] = 0;
q.push(1);
while (q.size())
{
int t = q.front();
q.pop();
for (int i = h[t]; i != -1; i = ne[i])
{
int j = e[i];
if (d[j] == -1) //表示j这个点没有访问过
{
d[j] = d[t] + 1;
q.push(j);
}
}
}
return d[n];
}
int main()
{
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
for (int i = 0; i < m; i ++ )
{
int a, b;
scanf("%d%d", &a, &b);
add(a, b); //有向图就设置成单条边
}
cout << bfs() << endl; //直接输出最短路径
return 0;
}
有向图的拓扑序列
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=100010;
int n,m;
int idx,e[maxn],ne[maxn],h[maxn];
int q[maxn];
int d[maxn];
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
bool tuopu()
{
int hh=0,tt=-1; //hh表示开头,tt表示结尾
for(int i=1; i<=n; i++)
if(!d[i]) //统计入度为0的点
q[++tt]=i;
while(tt>=hh)
{
int t=q[hh++];
for(int i=h[t]; i!=-1; i=ne[i])
{
int j=e[i];
d[j]--;
if(d[j]==0)
{
q[++tt]=j;
}
}
}
if(tt==n-1) //如果tt==n-1则返回true
return true;
}
int main()
{
cin >> n >> m;
memset(h,-1,sizeof h);
for(int i=0; i<m; i++)
{
int a,b;
cin >> a >> b;
add(a,b);
d[b]++; //统计每个点的入度个数
}
if(!tuopu())
cout << -1;
else
{
for(int i=0; i<n; i++)
cout << q[i] << " ";
}
return 0;
}
有边数限制的最短路
#include<iostream>
#include<cstring>
using namespace std;
const int N = 510, M = 10010; //注意边数的范围
struct Edge
{
int a;
int b;
int w;
} e[M];
int dist[N];
int back[N];
int n, m, k;
int bellman_ford()
{
memset(dist, 0x3f, sizeof dist); //对dist初始化为0x3f
dist[1] = 0;
for (int i = 0; i < k; i++) //最多经过的边数是k条边,所以就循环k次
{
memcpy(back, dist, sizeof dist); //这里需要备份,因为我们循环其实是k条边,具体原因也不用管,将上一次的dist备份到back
for (int j = 0; j < m; j++)
{
int a = e[j].a, b = e[j].b, w = e[j].w;
dist[b] = min(dist[b], back[a] + w); //注意这里是back[a]+w,不是dist[a]+w
}
}
if (dist[n] > 0x3f3f3f3f / 2) return -1 //之所以大于0x3f3f3f3f/2是因为如果倒数第2个点可以更新倒数第1个点,就是表示先到倒2点后从倒2进入倒1,那么倒1的点dist可能表示的比0x3f3f3f3f小一点
else return dist[n];
}
int main()
{
scanf("%d%d%d", &n, &m, &k); //输入n个点,m条边,k个限制
for (int i = 0; i < m; i++)
{
int a, b, w;
scanf("%d%d%d", &a, &b, &w);
e[i] = {a, b, w}; //搞一个结构
}
int res = bellman_ford(); //res表示最终的结果
if (res == -1)
puts("impossible");
else
cout << res;
return 0;
}
spfa求最短路
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int maxn=100010;
int n,m;
int idx,h[maxn],ne[maxn],w[maxn],e[maxn];
int dist[maxn];
bool st[maxn];
void add(int a,int b,int c)
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
int spfa()
{
memset(dist,0x3f,sizeof dist);
dist[1]=0;
queue<int> q;
q.push(1);
st[1]=true;
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];
if(!st[j])
{
q.push(j);
st[j]=true;
}
}
}
}
return dist[n];
}
int main()
{
cin >> n >> m;
memset(h,-1,sizeof h);
for(int i=0; i<m; i++)
{
int a,b,c;
cin >> a >> b >> c;
add(a,b,c);
}
int t=spfa();
if(t==0x3f3f3f3f)
cout << "impossible" << endl;
else
cout << t << endl;
return 0;
}
spfa判断负环
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 2010, M = 10010;
int n, m;
int h[N], w[M], e[M], ne[M], idx;
int dist[N], cnt[N]; //dist[x]表示1~x的最短距离,cnt[x]表示当前最短路的边数
bool st[N];
void add(int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
bool spfa()
{
queue<int> q; //设置队列q
//问题求的是去判断能否有负环,并不一定要从1号点开始,所以需要将所有点放入队列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; //cnt[j]>=n,表示从1--j所经过的边数>=n,表示出现了负环,否则表示没有出现负环
if (!st[j])
{
q.push(j);
st[j] = true;
}
}
}
}
return false;
}
int main()
{
scanf("%d%d", &n, &m); //n个点,m条边
memset(h, -1, sizeof h); //h 初始化为-1
while (m -- )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
}
if (spfa()) puts("Yes");
else puts("No");
return 0;
}
Dijkstra求最短路 I
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=511;
int dist[maxn];
int g[maxn][maxn];
int n,m;
bool st[maxn];
int dijkstra()
{
memset(dist,0x3f,sizeof dist);
dist[1]=0;
for(int i=1; i<=n-1; 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++)
if(dist[j]>dist[t]+g[t][j])
dist[j]=dist[t]+g[t][j];
}
if(dist[n]==0x3f3f3f3f)
return -1;
return dist[n];
}
int main()
{
cin >> n >> m;
memset(g,0x3f,sizeof g);
for(int i=0; i<m; i++)
{
int a,b,c;
cin >> a >> b >> c;
g[a][b]=min(g[a][b],c);
}
cout << dijkstra() << endl;
return 0;
}
Dijkstra求最短路 2
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int maxn=150010;
typedef pair<int,int> PII;
int n,m;
int idx,h[maxn],ne[maxn],e[maxn],w[maxn];
int dist[maxn];
bool st[maxn];
void add(int a,int b,int c)
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
int dijkstra()
{
memset(dist,0x3f,sizeof dist);
dist[1]=0;
priority_queue<PII ,vector<PII>,greater<PII>> heap; //优先队列
heap.push({0,1}); //到1这个点,距离为0
while(heap.size())
{
auto t=heap.top();
heap.pop();
int ver=t.second; //ver表示中间的点
if(st[ver])
continue;
st[ver]=true;
for(int i=h[ver]; i!=-1; i=ne[i])
{
int j=e[i];
if(dist[j]>dist[ver]+w[i])
{
dist[j]=dist[ver]+w[i];
heap.push({dist[j],j});
}
}
}
if(dist[n]==0x3f3f3f3f)
return -1;
return dist[n];
}
int main()
{
cin >> n >> m;
memset(h,-1,sizeof h);
for(int i=0; i<m; i++)
{
int a,b,c;
cin >> a >> b >> c; //输入a,b,c
add(a,b,c);
}
cout << dijkstra(); //直接输出
return 0;
}
Floyd求最短路
#include<iostream>
using namespace std;
const int maxn=211;
const int eps=1e9;
int n,m,Q;
int d[maxn][maxn];
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 >> Q;
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]=eps;
for(int i=0; i<m; i++)
{
int a,b,c;
cin >> a >> b >> c;
d[a][b]=min(d[a][b],c);
}
floyd();
while(Q--)
{
int a,b;
cin >> a >> b;
int t=d[a][b];
if(t>eps/2)
cout << "impossible" << endl;
else
cout << t << endl;
}
return 0;
}
Prim算法求最小生成树
选中第1个点
用该点去更新其他点
选择距离1号点距离最近的点,并且用2号点去更新其他点的距离
找到这里面距离最近的点,也就是3号点
最后一步将4选中
最后的生成树
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 510, INF = 0x3f3f3f3f;
int n, m;
int g[N][N];
int dist[N];
bool st[N];
int prim()
{
memset(dist, 0x3f, sizeof dist); //将所有距离初始化为正无穷
int res = 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;
if (i && dist[t] == INF) return INF; //如果不是第1个点,并且如果dist[n]为正无穷,表示没有找到合适的点,则直接返回正无穷
if (i) res += dist[t]; //只要不是第1条边,则将dist[t
st[t] = true;
for (int j = 1; j <= n; j ++ )
dist[j] = min(dist[j], g[t][j]); //用t更新其他点到集合的距离
}
return res;
}
int main()
{
scanf("%d%d", &n, &m); //n个点,m条边
memset(g, 0x3f, sizeof g);
while (m -- )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
g[a][b] = g[b][a] = min(g[a][b], c);
}
int t = prim(); //t表示最小生成树的边权
if (t == INF) puts("impossible");
else printf("%d\n", t);
return 0;
}
Kruskal算法求最小生成树
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
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; //cnt表示边数,res表示边权
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; //n个点至少需要n-1条边,如果计算的生成树中的边数小于n-1则直接返回INF,表示没有最小生成树
return res;
}
int main()
{
scanf("%d%d", &n, &m); //n个点,m条边
for (int i = 0; i < m; i ++ ) //结构来存放边的信息
{
int a, b, w;
scanf("%d%d%d", &a, &b, &w); //输入a,b点和边权
edges[i] = {a, b, w};
}
int t = kruskal(); //调用kruskal()
if (t == INF) puts("impossible");
else printf("%d\n", t);
return 0;
}
染色法判定二分图
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010, M = 200010;
int n, m;
int h[N], e[M], ne[M], idx;
int color[N];
void add(int a, int b) //邻接表存储
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
bool dfs(int u, int c)
{
color[u] = c; //标记u的颜色为c
for (int i = h[u]; i != -1; i = ne[i])
{
int j = e[i];
if (!color[j]) //如果当前这个点没有染过颜色
{
if (!dfs(j, 3 - c)) return false; //则需要将j对应的点染成另一种颜色
}
else if (color[j] == c) return false; //如果一条边上的两种颜色相同,也是返回false
}
return true;
}
int main()
{
scanf("%d%d", &n, &m); //输入n个点,m条边
memset(h, -1, sizeof h);
while (m -- )
{
int a, b;
scanf("%d%d", &a, &b);
add(a, b), add(b, a); //双向设置
}
bool flag = true; //初始化flag为true,表示没有矛盾发生
for (int i = 1; i <= n; i ++ ) //遍历n个点
if (!color[i]) //如果i没有染色
{
if (!dfs(i, 1)) //用dfs将i所在的连通块进行全部染色
{
flag = false; //如果出现矛盾,则让flag为false
break;
}
}
if (flag) puts("Yes"); //flag为true,直接输出yes;否则输出no
else puts("No");
return 0;
}
二分图的最大匹配
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 510, M = 100010;
int n1, n2, m;
int h[N], e[M], ne[M], idx;
int match[N]; //右边对应的点
bool st[N];
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
bool find(int x)
{
for (int i = h[x]; i != -1; i = ne[i])
{
int j = e[i];
if (!st[j]) //这个妹子之前没有考虑过
{
st[j] = true;
if (match[j] == 0 || find(match[j])) //如果这个妹子没有匹配任何男生,或者可以为追求该妹子的男的找到下家
{
match[j] = x; //则该妹子可以匹配x
return true;
}
}
}
return false;
}
int main()
{
scanf("%d%d%d", &n1, &n2, &m); //n1,n2点,m条边
memset(h, -1, sizeof h); //h表示-1
while (m -- )
{
int a, b;
scanf("%d%d", &a, &b);
add(a, b);
}
int res = 0; //匹配的数量
for (int i = 1; i <= n1; i ++ ) //枚举左边的点
{
memset(st, false, sizeof st); //将所有妹子清空
if (find(i)) res ++ ;
}
printf("%d\n", res);
return 0;
}