搜索与图论模板题(必备)Day3
DFS
给定一个整数 nn,将数字 1∼n1∼n 排成一排,将会有很多种排列方法。
现在,请你按照字典序将所有的排列方法输出。
输入格式
共一行,包含一个整数 nn。
输出格式
按字典序输出所有排列方案,每个方案占一行。
数据范围
1≤n≤71≤n≤7
输入样例:
3
输出样例:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
#include<iostream>
#include<algorithm>
#include<math.h>
#include<string>
#include<list>
#include<map>
#include<queue>
#include<vector>
#include<stack>
using namespace std;
const int MM=2147483647;
const int NN=-2147483648;
const long MMM=9223372036854775807;
const long NNN=-9223372036854775808;
#define ll long long
const int N=10;
int n;
int path[N];
int state[N];
typedef pair<int,int>PII;
void dfs(int u){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;
}
n−n−皇后问题是指将 nn 个皇后放在 n×nn×n 的国际象棋棋盘上,使得皇后不能相互攻击到,即任意两个皇后都不能处于同一行、同一列或同一斜线上。
现在给定整数 nn,请你输出所有的满足条件的棋子摆法。
输入格式
共一行,包含整数 nn。
输出格式
每个解决方案占 nn 行,每行输出一个长度为 nn 的字符串,用来表示完整的棋盘状态。
其中 .
表示某一个位置的方格状态为空,Q
表示某一个位置的方格上摆着皇后。
每个方案输出完成后,输出一个空行。
注意:行末不能有多余空格。
输出方案的顺序任意,只要不重复且没有遗漏即可。
数据范围
1≤n≤91≤n≤9
输入样例:
4
输出样例:
.Q..
...Q
Q...
..Q...Q.
Q...
...Q
.Q..
#include <iostream>
using namespace std;
const int N = 20; // bool数组用来判断搜索的下一个位置是否可行
// col列,dg对角线,udg反对角线
// g[N][N]用来存路径int n;
char g[N][N];
bool col[N], dg[N], udg[N];void dfs(int u) {// u == n 表示已经搜了n行,故输出这条路径if (u == n) {for (int i = 0; i < n; i ++ ) puts(g[i]); // 等价于cout << g[i] << endl;puts(""); // 换行return;}// 枚举u这一行,搜索合法的列int x = u;for (int y = 0; y < n; y ++ )// 剪枝(对于不满足要求的点,不再继续往下搜索) if (col[y] == false && dg[y - x + n] == false && udg[y + x] == false) {col[y] = dg[y - x + n] = udg[y + x] = true;g[x][y] = 'Q';dfs(x + 1);g[x][y] = '.'; // 恢复现场col[y] = dg[y - x + n] = udg[y + x] = false;}
}int main() {cin >> n;for (int i = 0; i < n; i ++ )for (int j = 0; j < n; j ++ )g[i][j] = '.';dfs(0);return 0;
} 作者:白马金羁侠少年
链接:https://www.acwing.com/solution/content/2820/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
BFS
给定一个 n×mn×m 的二维整数数组,用来表示一个迷宫,数组中只包含 00 或 11,其中 00 表示可以走的路,11 表示不可通过的墙壁。
最初,有一个人位于左上角 (1,1)(1,1) 处,已知该人每次可以向上、下、左、右任意一个方向移动一个位置。
请问,该人从左上角移动至右下角 (n,m)(n,m) 处,至少需要移动多少次。
数据保证 (1,1)(1,1) 处和 (n,m)(n,m) 处的数字为 00,且一定至少存在一条通路。
输入格式
第一行包含两个整数 nn 和 mm。
接下来 nn 行,每行包含 mm 个整数(00 或 11),表示完整的二维数组迷宫。
输出格式
输出一个整数,表示从左上角移动至右下角的最少移动次数。
数据范围
1≤n,m≤1001≤n,m≤100
输入样例:
5 5
0 1 0 0 0
0 1 0 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0
输出样例:
8
#include <bits/stdc++.h>
using namespace std;typedef pair<int, int> PII;const int N = 1e2 + 7;
int g[N][N], d[N][N];
int n, m;int bfs() {int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};queue <PII> q;for (auto &v : d) for (auto &x : v) {x = - 1;}d[0][0] = 0;q.push({0, 0});while (!q.empty()) {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) {d[x][y] = d[t.first][t.second] + 1;q.push({x, y});}}}return d[n - 1][m - 1];
}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;
}作者:白马金羁侠少年
链接:https://www.acwing.com/solution/content/2078/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
在一个 3×33×3 的网格中,1∼81∼8 这 88 个数字和一个 x
恰好不重不漏地分布在这 3×33×3 的网格中。
例如:
1 2 3
x 4 6
7 5 8
在游戏过程中,可以把 x
与其上、下、左、右四个方向之一的数字交换(如果存在)。
我们的目的是通过交换,使得网格变为如下排列(称为正确排列):
1 2 3
4 5 6
7 8 x
例如,示例中图形就可以通过让 x
先后与右、下、右三个方向的数字交换成功得到正确排列。
交换过程如下:
1 2 3 1 2 3 1 2 3 1 2 3
x 4 6 4 x 6 4 5 6 4 5 6
7 5 8 7 5 8 7 x 8 7 8 x
现在,给你一个初始网格,请你求出得到正确排列至少需要进行多少次交换。
输入格式
输入占一行,将 3×33×3 的初始网格描绘出来。
例如,如果初始网格如下所示:
1 2 3
x 4 6
7 5 8
则输入为:1 2 3 x 4 6 7 5 8
输出格式
输出占一行,包含一个整数,表示最少交换次数。
如果不存在解决方案,则输出 −1−1。
输入样例:
2 3 4 1 5 x 7 6 8
输出样例
19
#include <iostream>
#include <algorithm>
#include <queue>
#include <unordered_map>using namespace std;int bfs(string start)
{//定义目标状态string end = "12345678x";//定义队列和dist数组queue<string> q;unordered_map<string, int> d;//初始化队列和dist数组q.push(start);d[start] = 0;//转移方式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;//查询x在字符串中的下标,然后转换为在矩阵中的坐标int k = t.find('x');int x = k / 3, y = k % 3;for(int i = 0; i < 4; i++){//求转移后x的坐标int a = x + dx[i], b = y + dy[i];//当前坐标没有越界if(a >= 0 && a < 3 && b >= 0 && b < 3){//转移xswap(t[k], t[a * 3 + b]);//如果当前状态是第一次遍历,记录距离,入队if(!d.count(t)){d[t] = distance + 1;q.push(t);}//还原状态,为下一种转换情况做准备swap(t[k], t[a * 3 + b]);}}}//无法转换到目标状态,返回-1return -1;
}int main()
{string c, start;//输入起始状态for(int i = 0; i < 9; i++){cin >> c;start += c;}cout << bfs(start) << endl;return 0;
}作者:四谷夕雨
链接:https://www.acwing.com/solution/content/15149/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
树与图的深度优先遍历
给定一颗树,树中包含 nn 个结点(编号 1∼n1∼n)和 n−1n−1 条无向边。
请你找到树的重心,并输出将重心删除后,剩余各个连通块中点数的最大值。
重心定义:重心是指树中的一个结点,如果将这个点删除后,剩余各个连通块中点数的最大值最小,那么这个节点被称为树的重心。
输入格式
第一行包含整数 nn,表示树的结点数。
接下来 n−1n−1 行,每行包含两个整数 aa 和 bb,表示点 aa 和点 bb 之间存在一条边。
输出格式
输出一个整数 mm,表示将重心删除后,剩余各个连通块中点数的最大值。
数据范围
1≤n≤1051≤n≤105
输入样例
9
1 2
1 7
1 4
2 8
2 5
4 3
3 9
4 6
输出样例:
4
#include <iostream>
#include <algorithm>
#include <cstring>using namespace std;const int N = 1e5 + 10; //数据范围是10的5次方
const int M = 2 * N; //以有向图的格式存储无向图,所以每个节点至多对应2n-2条边int h[N]; //邻接表存储树,有n个节点,所以需要n个队列头节点
int e[M]; //存储元素
int ne[M]; //存储列表的next值
int idx; //单链表指针
int n; //题目所给的输入,n个节点
int ans = N; //表示重心的所有的子树中,最大的子树的结点数目bool st[N]; //记录节点是否被访问过,访问过则标记为true//a所对应的单链表中插入b a作为根
void add(int a, int b) {e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}// dfs 框架
/*
void dfs(int u){st[u]=true; // 标记一下,记录为已经被搜索过了,下面进行搜索过程for(int i=h[u];i!=-1;i=ne[i]){int j=e[i];if(!st[j]) {dfs(j);}}
}
*///返回以u为根的子树中节点的个数,包括u节点
int dfs(int u) {int res = 0; //存储 删掉某个节点之后,最大的连通子图节点数st[u] = true; //标记访问过u节点int sum = 1; //存储 以u为根的树 的节点数, 包括u,如图中的4号节点//访问u的每个子节点for (int i = h[u]; i != -1; i = ne[i]) {int j = e[i];//因为每个节点的编号都是不一样的,所以 用编号为下标 来标记是否被访问过if (!st[j]) {int s = dfs(j); // u节点的单棵子树节点数 如图中的size值res = max(res, s); // 记录最大联通子图的节点数sum += s; //以j为根的树 的节点数}}//n-sum 如图中的n-size值,不包括根节点4;res = max(res, n - sum); // 选择u节点为重心,最大的 连通子图节点数ans = min(res, ans); //遍历过的假设重心中,最小的最大联通子图的 节点数return sum;
}int main() {memset(h, -1, sizeof h); //初始化h数组 -1表示尾节点cin >> n; //表示树的结点数// 题目接下来会输入,n-1行数据,// 树中是不存在环的,对于有n个节点的树,必定是n-1条边for (int i = 0; i < n - 1; i++) {int a, b;cin >> a >> b;add(a, b), add(b, a); //无向图}dfs(1); //可以任意选定一个节点开始 u<=ncout << ans << endl;return 0;
}
树与图的广度优先遍历
给定一个 nn 个点 mm 条边的有向图,图中可能存在重边和自环。
所有边的长度都是 11,点的编号为 1∼n1∼n。
请你求出 11 号点到 nn 号点的最短距离,如果从 11 号点无法走到 nn 号点,输出 −1−1。
输入格式
第一行包含两个整数 nn 和 mm。
接下来 mm 行,每行包含两个整数 aa 和 bb,表示存在一条从 aa 走到 bb 的长度为 11 的边。
输出格式
输出一个整数,表示 11 号点到 nn 号点的最短距离。
数据范围
1≤n,m≤1051≤n,m≤105
输入样例:
4 5
1 2
2 3
3 4
1 3
1 4
输出样例:
1
#include <cstring>
#include <iostream>using namespace std;const int N=1e5+10;int h[N], e[N], idx, ne[N];
int d[N]; //存储每个节点离起点的距离 d[1]=0
int n, m; //n个节点m条边
int q[N]; //存储层次遍历序列 0号节点是编号为1的节点void add(int a, int b)
{e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}int bfs()
{int hh=0,tt=0;q[0]=1; //0号节点是编号为1的节点memset(d,-1,sizeof d);d[1]=0; //存储每个节点离起点的距离//当我们的队列不为空时while(hh<=tt){//取出队列头部节点int t=q[hh++];//遍历t节点的每一个邻边for(int i=h[t];i!=-1;i=ne[i]){int j=e[i];//如果j没有被扩展过if(d[j]==-1){d[j]=d[t]+1; //d[j]存储j节点离起点的距离,并标记为访问过q[++tt] = j; //把j结点 压入队列}}}return d[n];
}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);}cout<<bfs()<<endl;
}作者:松鼠爱葡萄
链接:https://www.acwing.com/solution/content/13514/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
拓扑排序
给定一个 nn 个点 mm 条边的有向图,点的编号是 11 到 nn,图中可能存在重边和自环。
请输出任意一个该有向图的拓扑序列,如果拓扑序列不存在,则输出 −1−1。
若一个由图中所有点构成的序列 AA 满足:对于图中的每条边 (x,y)(x,y),xx 在 AA 中都出现在 yy 之前,则称 AA 是该图的一个拓扑序列。
输入格式
第一行包含两个整数 nn 和 mm。
接下来 mm 行,每行包含两个整数 xx 和 yy,表示存在一条从点 xx 到点 yy 的有向边 (x,y)(x,y)。
输出格式
共一行,如果存在拓扑序列,则输出任意一个合法的拓扑序列即可。
否则输出 −1−1。
数据范围
1≤n,m≤1051≤n,m≤105
输入样例:
3 3
1 2
2 3
1 3
输出样例:
1 2 3
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;const int N=100010;
int h[N],e[N],ne[N],idx;
int n,m;
int q[N],d[N];//q表示队列,d表示点的入度void add(int a,int b)
{e[idx]=b;ne[idx]=h[a];h[a]=idx++;
}bool topsort()
{int hh=0,tt=-1;for(int i=1;i<=n;i++)if(!d[i]) q[++tt]=i;//将入度为零的点入队while(hh<=tt){int t=q[hh++];for(int i=h[t];i!=-1;i=ne[i]){int j=e[i];d[j]--;//删除点t指向点j的边if(d[j]==0)//如果点j的入度为零了,就将点j入队q[++tt]=j;}}return tt==n-1;//表示如果n个点都入队了话,那么该图为拓扑图,返回true,否则返回false
}int main()
{cin>>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);//因为是a指向b,所以b点的入度要加1d[b]++;}if(topsort()) {for(int i=0;i<n;i++)printf("%d ",q[i]);//经上方循环可以发现队列中的点的次序就是拓扑序列//注:拓扑序列的答案并不唯一,可以从解析中找到解释puts("");}elseputs("-1");return 0;
}作者:E.lena
链接:https://www.acwing.com/solution/content/21908/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
Dijkstra
给定一个 nn 个点 mm 条边的有向图,图中可能存在重边和自环,所有边权均为正值。
请你求出 11 号点到 nn 号点的最短距离,如果无法从 11 号点走到 nn 号点,则输出 −1−1。
输入格式
第一行包含整数 nn 和 mm。
接下来 mm 行每行包含三个整数 x,y,zx,y,z,表示存在一条从点 xx 到点 yy 的有向边,边长为 zz。
输出格式
输出一个整数,表示 11 号点到 nn 号点的最短距离。
如果路径不存在,则输出 −1−1。
数据范围
1≤n≤5001≤n≤500,
1≤m≤1051≤m≤105,
图中涉及边长均不超过10000。
输入样例:
3 3
1 2 2
2 3 1
1 3 4
输出样例:
3
有之前宽搜和深搜的基础 再听了y总的课 就感觉思路特别清晰了
最近复习了下Dijkstra 发现之前的理解还是有些不太到位
所以更新了一下 希望能帮助一起学算法的小伙伴理清思路
Dijkstra 的整体思路比较清晰
即进行n(n为n的个数)次迭代去确定每个点到起点的最小值 最后输出的终点的即为我们要找的最短路的距离
所以按照这个思路除了存储图外我们还需要存储两个量
dist[n] //用于存储每个点到起点的最短距离
st[n] //用于在更新最短距离时 判断当前的点的最短距离是否确定 是否需要更新
每次迭代的过程中我们都先找到当前未确定的最短距离的点中距离最短的点
(至于为什么是这样那么这就涉及到Dijkstra算法的具体数学证明了 有兴趣的同学可以百度一下)
int t=-1; //将t设置为-1 因为Dijkstra算法适用于不存在负权边的图
for(int j=1;j<=n;j++)
{
if(!st[j]&&(t==-1||dist[t]>dist[j]) //该步骤即寻找还未确定最短路的点中路径最短的点
t=j;
}
通过上述操作当前我们的t代表就是剩余未确定最短路的点中 路径最短的点
而与此同时该点的最短路径也已经确定我们将该点标记
st[t]=true;
然后用这个去更新其余未确定点的最短距离
for(int j=1;j<=n;j++)
dist[j]=min(dist[j],dist[t]+g[t][j]);
//这里可能有同学要问j如果从1开始的话 会不会影响之前已经确定的点的最小距离
//但其实是不会 因为按照我们的Dijkstra算法的操作顺序 先确定最短距离的点的距离已经比后确定的要小 所以不会影响
//当然你也可以在循环判断条件里加上if(!st[i])
//这里j从1开始只是为了代码的简洁
进行n次迭代后最后就可以确定每个点的最短距离
然后再根据题意输出相应的 要求的最短距离
以下为完整代码
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=510;
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;
}
作者:Ni
链接:https://www.acwing.com/solution/content/5806/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
给定一个 nn 个点 mm 条边的有向图,图中可能存在重边和自环,所有边权均为非负值。
请你求出 11 号点到 nn 号点的最短距离,如果无法从 11 号点走到 nn 号点,则输出 −1−1。
输入格式
第一行包含整数 nn 和 mm。
接下来 mm 行每行包含三个整数 x,y,zx,y,z,表示存在一条从点 xx 到点 yy 的有向边,边长为 zz。
输出格式
输出一个整数,表示 11 号点到 nn 号点的最短距离。
如果路径不存在,则输出 −1−1。
数据范围
1≤n,m≤1.5×1051≤n,m≤1.5×105,
图中涉及边长均不小于 00,且不超过 1000010000。
数据保证:如果最短路存在,则最短路的长度不超过 109109。
输入样例:
3 3
1 2 2
2 3 1
1 3 4
输出样例:
3
算法分析
注意:若要求任意点i到任意个点j的最短距离,只需修改dijkstra方法中的起源位置dist[i] = 0,以及返回为dist[j]
时间复杂度 O(mlogn)O(mlogn)
每次找到最小距离的点沿着边更新其他的点,若dist[j] > distance + w[i],表示可以更新dist[j],更新后再把j点和对应的距离放入小根堆中。由于点的个数是n,边的个数是m,在极限情况下(稠密图m=n(n−1)2m=n(n−1)2)最多可以更新m回,每一回最多可以更新nn个点(严格上是n - 1个点),有m回,因此最多可以把n2n2个点放入到小根堆中,因此每一次更新小根堆排序的情况是O(log(n2))O(log(n2)),一共最多m次更新,因此总的时间复杂度上限是O(mlog((n2)))=O(2mlogn)=O(mlogn)O(mlog((n2)))=O(2mlogn)=O(mlogn)
Java 代码
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.PriorityQueue;
public class Main{
static int N = 100010;
static int n;
static int[] h = new int[N];
static int[] e = new int[N];
static int[] ne = new int[N];
static int[] w = new int[N];
static int idx = 0;
static int[] dist = new int[N];// 存储1号点到每个点的最短距离
static boolean[] st = new boolean[N];
static int INF = 0x3f3f3f3f;//设置无穷大
public static void add(int a,int b,int c)
{
e[idx] = b;
w[idx] = c;
ne[idx] = h[a];
h[a] = idx ++;
}
// 求1号点到n号点的最短路,如果不存在则返回-1
public static int dijkstra()
{
//维护当前未在st中标记过且离源点最近的点
PriorityQueue<PIIs> queue = new PriorityQueue<PIIs>();
Arrays.fill(dist, INF);
dist[1] = 0;
queue.add(new PIIs(0,1));
while(!queue.isEmpty())
{
//1、找到当前未在s中出现过且离源点最近的点
PIIs p = queue.poll();
int t = p.getSecond();
int distance = p.getFirst();
if(st[t]) continue;
//2、将该点进行标记
st[t] = true;
//3、用t更新其他点的距离
for(int i = h[t];i != -1;i = ne[i])
{
int j = e[i];
if(dist[j] > distance + w[i])
{
dist[j] = distance + w[i];
queue.add(new PIIs(dist[j],j));
}
}
}
if(dist[n] == INF) return -1;
return dist[n];
}
public static void main(String[] args) throws IOException{
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String[] str1 = reader.readLine().split(" ");
n = Integer.parseInt(str1[0]);
int m = Integer.parseInt(str1[1]);
Arrays.fill(h, -1);
while(m -- > 0)
{
String[] str2 = reader.readLine().split(" ");
int a = Integer.parseInt(str2[0]);
int b = Integer.parseInt(str2[1]);
int c = Integer.parseInt(str2[2]);
add(a,b,c);
}
System.out.println(dijkstra());
}
}
class PIIs implements Comparable<PIIs>{
private int first;//距离值
private int second;//点编号
public int getFirst()
{
return this.first;
}
public int getSecond()
{
return this.second;
}
public PIIs(int first,int second)
{
this.first = first;
this.second = second;
}
@Override
public int compareTo(PIIs o) {
// TODO 自动生成的方法存根
return Integer.compare(first, o.first);
}
}
作者:小呆呆
链接:https://www.acwing.com/solution/content/6291/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
代码有误,下面是正确代码
#include <iostream>
#include <vector>
#include <queue>
#include <memory.h>using namespace std;
typedef pair<int, int> PII;const int N = 100010;
vector<vector<pair<int, int>>> gra;
int dist[N];
int st[N];
int n, m;int solve()
{memset(dist, 0x3f, sizeof dist);dist[1] = 0;//这里是距离在前 节点号在后priority_queue<PII, vector<PII>, greater<PII>> heap;heap.push({ 0, 1 }); // first存储距离,second存储节点编号while (heap.size()) {auto t = heap.top();heap.pop();int node = t.second; int distance = t.first;if (st[node]) continue;st[node] = true;//查看每个出边for (int i = 0; i < gra[node].size(); i++) {int newnode = gra[node][i].first;int len = gra[node][i].second;if (dist[newnode] > dist[node] + len) {dist[newnode] = dist[node] + len;heap.push({ dist[newnode],newnode });}}}if (dist[n] == 0x3f3f3f3f) return -1;return dist[n];
}int main()
{//cin >> n >> m;scanf("%d %d",&n,&m);gra.resize(n + 1);for (int i = 0; i < m; i++) {int a, b, c;//cin >> a >> b >> c;scanf("%d %d %d",&a,&b,&c);//这里是 目的节点号在前 边长在后gra[a].push_back({ b,c });}printf("%d\n", solve() );return 0;
}作者:itdef
链接:https://www.acwing.com/solution/content/7074/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
bellman-ford
有边数限制的最短路
给定一个 nn 个点 mm 条边的有向图,图中可能存在重边和自环, 边权可能为负数。
请你求出从 11 号点到 nn 号点的最多经过 kk 条边的最短距离,如果无法从 11 号点走到 nn 号点,输出 impossible
。
注意:图中可能 存在负权回路 。
输入格式
第一行包含三个整数 n,m,kn,m,k。
接下来 mm 行,每行包含三个整数 x,y,zx,y,z,表示存在一条从点 xx 到点 yy 的有向边,边长为 zz。
点的编号为 1∼n1∼n。
输出格式
输出一个整数,表示从 11 号点到 nn 号点的最多经过 kk 条边的最短距离。
如果不存在满足条件的路径,则输出 impossible
。
数据范围
1≤n,k≤5001≤n,k≤500,
1≤m≤100001≤m≤10000,
1≤x,y≤n1≤x,y≤n,
任意边长的绝对值不超过 1000010000。
输入样例:
3 3 1
1 2 1
2 3 1
1 3 3
输出样例:
3
算法分析
1、问题:为什么Dijkstra不能使用在含负权的图中?(在强大的网友指点下已进行修改)
(这是以前错误的分析,若看完这个例子分析觉得正确的说明对最短路理解得还不够透彻,这里不做删除)
分析:如图所示:
若通过Dijkstra算法可以求出从1号点到达4号点所需的步数为3 (每次选择离源点最短距离的点更新其他点)
但实际上从 1 号点到达 4 号点所需步数为 1 (1 –> 2 –> 3),因此不能使用 Dijkstra 解决含负权图的问题
正确的分析
Dijkstra算法的3个步骤
1、找到当前未标识的且离源点最近的点t
2、对t号点点进行标识
3、用t号点更新其他点的距离
反例
结果:
dijkstra算法在图中走出来的最短路径是1 -> 2 -> 4 -> 5,算出 1 号点到 5 号点的最短距离是2 + 2 + 1 = 5,然而还存在一条路径是1 -> 3 -> 4 -> 5,该路径的长度是5 + (-2) + 1 = 4,因此 dijkstra 算法失效
dijkstra详细步骤
初始dist[1] = 0
找到了未标识且离源点1最近的结点1,标记1号点,用1号点更新其他所有点的距离,2号点被更新成dist[2] = 2,3号点被更新成dist[3] = 5
找到了未标识且离源点1最近的结点2,标识2号点,用2号点更新其他所有点的距离,4号点被更新成dist[4] = 4
找到了未标识且离源点1最近的结点4,标识4号点,用4号点更新其他所有点的距离,5号点被更新成dist[5] = 5
找到了未标识且离源点1最近的结点3,标识3号点,用3号点更新其他所有点的距离,4号点被更新成dist[4] = 3
结束
得到1号点到5号点的最短距离是5,对应的路径是1 -> 2 -> 4 -> 5,并不是真正的最短距离
2、什么是bellman - ford算法?
Bellman - ford 算法是求含负权图的单源最短路径的一种算法,效率较低,代码难度较小。其原理为连续进行松弛,在每次松弛时把每条边都更新一下,若在 n-1 次松弛后还能更新,则说明图中有负环,因此无法得出结果,否则就完成。
(通俗的来讲就是:假设 1 号点到 n 号点是可达的,每一个点同时向指向的方向出发,更新相邻的点的最短距离,通过循环 n-1 次操作,若图中不存在负环,则 1 号点一定会到达 n 号点,若图中存在负环,则在 n-1 次松弛后一定还会更新)
3、bellman - ford算法的具体步骤
for n次
for 所有边 a,b,w (松弛操作)
dist[b] = min(dist[b],back[a] + w)
注意:back[] 数组是上一次迭代后 dist[] 数组的备份,由于是每个点同时向外出发,因此需要对 dist[] 数组进行备份,若不进行备份会因此发生串联效应,影响到下一个点
4、在下面代码中,是否能到达n号点的判断中需要进行if(dist[n] > INF/2)判断,而并非是if(dist[n] == INF)判断,原因是INF是一个确定的值,并非真正的无穷大,会随着其他数值而受到影响,dist[n]大于某个与INF相同数量级的数即可
5、bellman - ford算法擅长解决有边数限制的最短路问题
时间复杂度 O(nm)O(nm)
其中n为点数,m为边数
Java 代码
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
public class Main {
static int N = 510;
static int M = 100010;
static int n;//总点数
static int m;//总边数
static int k;//最多经过k条边
static int[] dist = new int[N];//从1到点到n号点的距离
static Node[] list = new Node[M];//结构体
static int INF = 0x3f3f3f3f;
static int[] back = new int[N];//备份dist数组
public static void bellman_ford()
{
Arrays.fill(dist, INF);
dist[1] = 0;
for(int i = 0;i < k;i++)
{
back = Arrays.copyOf(dist, n + 1);//由于是从1开始存到n
for(int j = 0;j < m;j++)
{
Node node = list[j];
int a = node.a;
int b = node.b;
int c = node.c;
dist[b] = Math.min(dist[b], back[a] + c);
}
}
if(dist[n] > INF/2) System.out.println("impossible");
else System.out.println(dist[n]);
}
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String[] str1 = reader.readLine().split(" ");
n = Integer.parseInt(str1[0]);
m = Integer.parseInt(str1[1]);
k = Integer.parseInt(str1[2]);
for(int i = 0;i < m;i++)
{
String[] str2 = reader.readLine().split(" ");
int a = Integer.parseInt(str2[0]);
int b = Integer.parseInt(str2[1]);
int c = Integer.parseInt(str2[2]);
list[i] = new Node(a,b,c);
}
bellman_ford();
}
}
class Node
{
int a, b, c;
public Node(int a,int b,int c)
{
this.a = a;
this.b = b;
this.c = c;
}
}
spfa
给定一个 nn 个点 mm 条边的有向图,图中可能存在重边和自环, 边权可能为负数。
请你求出 11 号点到 nn 号点的最短距离,如果无法从 11 号点走到 nn 号点,则输出 impossible
。
数据保证不存在负权回路。
输入格式
第一行包含整数 nn 和 mm。
接下来 mm 行每行包含三个整数 x,y,zx,y,z,表示存在一条从点 xx 到点 yy 的有向边,边长为 zz。
输出格式
输出一个整数,表示 11 号点到 nn 号点的最短距离。
如果路径不存在,则输出 impossible
。
数据范围
1≤n,m≤1051≤n,m≤105,
图中涉及边长绝对值均不超过 1000010000。
输入样例:
3 3
1 2 5
2 3 -3
1 3 4
输出样例:
2
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;public class Main {static int N = 100010;static int n;static int m;static int[] h = new int[N];static int[] e = new int[N];static int[] ne = new int[N];static int[] w = new int[N];static int idx = 0;static int[] dist = new int[N];static boolean[] st = new boolean[N]; //标记是否在队列中static int INF = 0x3f3f3f3f;public static void add(int a,int b,int c){e[idx] = b;w[idx] = c;ne[idx] = h[a];h[a] = idx ++;}public static int spfa(){Arrays.fill(dist, INF);Queue<Integer> queue = new LinkedList<Integer>();dist[1] = 0;queue.add(1);st[1] = true;//标记1号点在队列中while(!queue.isEmpty()){int t = queue.poll();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]){queue.add(j);st[j] = true;//标记已加入队列}}}}return dist[n];}public static void main(String[] args) throws IOException {BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));String[] str1 = reader.readLine().split(" ");n = Integer.parseInt(str1[0]);m = Integer.parseInt(str1[1]);Arrays.fill(h, -1);while(m -- > 0){String[] str2 = reader.readLine().split(" ");int a = Integer.parseInt(str2[0]);int b = Integer.parseInt(str2[1]);int c = Integer.parseInt(str2[2]);add(a,b,c);}int t = spfa();if(t == 0x3f3f3f3f) System.out.println("impossible");else System.out.println(t);}}作者:小呆呆
链接:https://www.acwing.com/solution/content/6325/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
给定一个 nn 个点 mm 条边的有向图,图中可能存在重边和自环, 边权可能为负数。
请你判断图中是否存在负权回路。
输入格式
第一行包含整数 nn 和 mm。
接下来 mm 行每行包含三个整数 x,y,zx,y,z,表示存在一条从点 xx 到点 yy 的有向边,边长为 zz。
输出格式
如果图中存在负权回路,则输出 Yes
,否则输出 No
。
数据范围
1≤n≤20001≤n≤2000,
1≤m≤100001≤m≤10000,
图中涉及边长绝对值均不超过 1000010000。
输入样例:
3 3
1 2 -1
2 3 4
3 1 -4
输出样例:
Yes
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;public class Main {static int n;static int m;static int N = 2010;static int M = 10010;static int[] h = new int[N];static int[] e = new int[M];static int[] ne = new int[M];static int[] w = new int[M];static int idx = 0;static int[] dist = new int[N];//记录虚拟点到x的最短距离static int[] cnt = new int[N];//从虚拟点到x经过的边数 static boolean[] st = new boolean[N];public static void add(int a,int b,int c){e[idx] = b;w[idx] = c;ne[idx] = h[a];h[a] = idx ++;}public static boolean spfa(){Queue<Integer> queue = new LinkedList<Integer>();//将所有点进入队列for(int i = 1;i <= n;i++){queue.add(i);st[i] = true;}while(!queue.isEmpty()){int t = queue.poll();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]){queue.add(j);st[j] = true;}}}}return false;}public static void main(String[] args) throws IOException {BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));String[] str1 = reader.readLine().split(" ");n = Integer.parseInt(str1[0]);m = Integer.parseInt(str1[1]);Arrays.fill(h, -1);while(m -- > 0){String[] str2 = reader.readLine().split(" ");int a = Integer.parseInt(str2[0]);int b = Integer.parseInt(str2[1]);int c = Integer.parseInt(str2[2]);add(a,b,c);}if(spfa()) System.out.println("Yes");else System.out.println("No");}}作者:小呆呆
链接:https://www.acwing.com/solution/content/6336/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
Floyd
给定一个 nn 个点 mm 条边的有向图,图中可能存在重边和自环,边权可能为负数。
再给定 kk 个询问,每个询问包含两个整数 xx 和 yy,表示查询从点 xx 到点 yy 的最短距离,如果路径不存在,则输出 impossible
。
数据保证图中不存在负权回路。
输入格式
第一行包含三个整数 n,m,kn,m,k。
接下来 mm 行,每行包含三个整数 x,y,zx,y,z,表示存在一条从点 xx 到点 yy 的有向边,边长为 zz。
接下来 kk 行,每行包含两个整数 x,yx,y,表示询问点 xx 到点 yy 的最短距离。
输出格式
共 kk 行,每行输出一个整数,表示询问的结果,若询问两点间不存在路径,则输出 impossible
。
数据范围
1≤n≤2001≤n≤200,
1≤k≤n21≤k≤n2
1≤m≤200001≤m≤20000,
图中涉及边长绝对值均不超过 1000010000。
输入样例:
3 3 2
1 2 1
2 3 2
1 3 1
2 1
1 3
输出样例:
impossible
1
#include <iostream>
using namespace std;const int N = 210, M = 2e+10, INF = 1e9;int n, m, k, x, y, z;
int d[N][N];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;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--) {cin >> x >> y >> z;d[x][y] = min(d[x][y], z);//注意保存最小的边}floyd();while(k--) {cin >> x >> y;if(d[x][y] > INF/2) puts("impossible");//由于有负权边存在所以约大过INF/2也很合理else cout << d[x][y] << endl;}return 0;
}作者:郡呈
链接:https://www.acwing.com/solution/content/6976/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
Prim
Prim算法求最小生成树
给定一个 nn 个点 mm 条边的无向图,图中可能存在重边和自环,边权可能为负数。
求最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible
。
给定一张边带权的无向图 G=(V,E)G=(V,E),其中 VV 表示图中点的集合,EE 表示图中边的集合,n=|V|n=|V|,m=|E|m=|E|。
由 VV 中的全部 nn 个顶点和 EE 中 n−1n−1 条边构成的无向连通子图被称为 GG 的一棵生成树,其中边的权值之和最小的生成树被称为无向图 GG 的最小生成树。
输入格式
第一行包含两个整数 nn 和 mm。
接下来 mm 行,每行包含三个整数 u,v,wu,v,w,表示点 uu 和点 vv 之间存在一条权值为 ww 的边。
输出格式
共一行,若存在最小生成树,则输出一个整数,表示最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible
。
数据范围
1≤n≤5001≤n≤500,
1≤m≤1051≤m≤105,
图中涉及边的边权的绝对值均不超过 1000010000。
输入样例:
4 5
1 2 1
1 3 2
1 4 3
2 3 2
3 4 4
输出样例:
6
//2022.6.1 更新#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;const int N = 510;
int g[N][N];//存储图
int dt[N];//存储各个节点到生成树的距离
int st[N];//节点是否被加入到生成树中
int pre[N];//节点的前去节点
int n, m;//n 个节点,m 条边void prim()
{memset(dt,0x3f, sizeof(dt));//初始化距离数组为一个很大的数(10亿左右)int res= 0;dt[1] = 0;//从 1 号节点开始生成 for(int i = 0; i < n; i++)//每次循环选出一个点加入到生成树{int t = -1;for(int j = 1; j <= n; j++)//每个节点一次判断{if(!st[j] && (t == -1 || dt[j] < dt[t]))//如果没有在树中,且到树的距离最短,则选择该点t = j;}//2022.6.1 发现测试用例加强后,需要判断孤立点了//如果孤立点,直返输出不能,然后退出if(dt[t] == 0x3f3f3f3f) {cout << "impossible";return;}st[t] = 1;// 选择该点res += dt[t];for(int i = 1; i <= n; i++)//更新生成树外的点到生成树的距离{if(dt[i] > g[t][i] && !st[i])//从 t 到节点 i 的距离小于原来距离,则更新。{dt[i] = g[t][i];//更新距离pre[i] = t;//从 t 到 i 的距离更短,i 的前驱变为 t.}}}cout << res;}void getPath()//输出各个边
{for(int i = n; i > 1; i--)//n 个节点,所以有 n-1 条边。{cout << i <<" " << pre[i] << " "<< endl;// i 是节点编号,pre[i] 是 i 节点的前驱节点。他们构成一条边。}
}int main()
{memset(g, 0x3f, sizeof(g));//各个点之间的距离初始化成很大的数cin >> n >> m;//输入节点数和边数while(m --){int a, b, w;cin >> a >> b >> w;//输出边的两个顶点和权重g[a][b] = g[b][a] = min(g[a][b],w);//存储权重}prim();//求最下生成树//getPath();//输出路径return 0;
}作者:Hasity
链接:https://www.acwing.com/solution/content/38312/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
Kruska
Kruskal算法求最小生成树
给定一个 nn 个点 mm 条边的无向图,图中可能存在重边和自环,边权可能为负数。
求最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible
。
给定一张边带权的无向图 G=(V,E)G=(V,E),其中 VV 表示图中点的集合,EE 表示图中边的集合,n=|V|n=|V|,m=|E|m=|E|。
由 VV 中的全部 nn 个顶点和 EE 中 n−1n−1 条边构成的无向连通子图被称为 GG 的一棵生成树,其中边的权值之和最小的生成树被称为无向图 GG 的最小生成树。
输入格式
第一行包含两个整数 nn 和 mm。
接下来 mm 行,每行包含三个整数 u,v,wu,v,w,表示点 uu 和点 vv 之间存在一条权值为 ww 的边。
输出格式
共一行,若存在最小生成树,则输出一个整数,表示最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible
。
数据范围
1≤n≤1051≤n≤105,
1≤m≤2∗1051≤m≤2∗105,
图中涉及边的边权的绝对值均不超过 10001000。
输入样例:
4 5
1 2 1
1 3 2
1 4 3
2 3 2
3 4 4
输出样例:
6
#include<iostream>
#include<algorithm>
#include<cstring>using namespace std;const int N=200010,M=100010; int p[M];
int n,m;struct Edge
{int a,b,w;bool operator< (const Edge &W)const{return w < W.w;}
}edges[N];int find(int x)
{if(p[x]!=x) p[x]=find(p[x]);else return x;
} int Kruskal()
{int res=0,cnt=0;//res记录最小生成树的树边权重之和,cnt记录的是全部加入到树的集合中边的数量(可能有多个集合)for(int i=0;i<m;i++){int a=edges[i].a,b=edges[i].b,w=edges[i].w;if(find(a)!=find(b))/*具体可以参考连通块中点的数量,如果a和b已经在一个集合当中了,说明这两个点已经被一种方式连接起来了,如果加入a-b这条边,会导致集合中有环的生成,而树中不允许有环生成,所以一个连通块中的点的数量假设为x,那么里面x个节点应该是被串联起来的,有x-1条边,所以只有当a,b所属的集合不同时,才能将a-b这条边加入到总集合当中去*/{p[find(a)]=p[find(b)];//将a,b所在的两个集合连接起来cnt++;//因为加入的是a-b的这一条边,将a,b所在的两个集合连接之后,全部集合中的边数加1res+=w;//加入到集合中的边的权重之和}}if(cnt==n-1) return res;//可以生成最小生成树else return 0x3f3f3f3f;//树中有n个节点便有n-1条边,如果cnt不等于n-1的话,说明无法生成有n个节点的树
}int main()
{cin>>n>>m;for(int i=0;i<n;i++) p[i]=i;//初始化并查集for(int i=0;i<m;i++){int a,b,w;scanf("%d%d%d",&a,&b,&w);edges[i]={a,b,w};}sort(edges,edges+m);//将边的权重按照大小一一排序int t=Kruskal();if(t==0x3f3f3f3f) printf("impossible\n");else printf("%d\n",t);return 0;
}作者:E.lena
链接:https://www.acwing.com/solution/content/20691/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
染色法判定二分图
给定一个 nn 个点 mm 条边的无向图,图中可能存在重边和自环。
请你判断这个图是否是二分图。
输入格式
第一行包含两个整数 nn 和 mm。
接下来 mm 行,每行包含两个整数 uu 和 vv,表示点 uu 和点 vv 之间存在一条边。
输出格式
如果给定图是二分图,则输出 Yes
,否则输出 No
。
数据范围
1≤n,m≤1051≤n,m≤105
输入样例:
4 4
1 3
1 4
2 3
2 4
输出样例:
Yes
#include <iostream>
#include <algorithm>
#include <cstring>using namespace std;
const int N = 1e5 + 10, M = 2e5 + 10; // 由于是无向图, 顶点数最大是N,那么边数M最大是顶点数的2倍
int e[M], ne[M], h[N], idx;
int st[N];void add(int a, int b){e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}bool dfs(int u, int color) {st[u] = color;for(int i = h[u]; i != -1; i = ne[i]){int j = e[i];if(!st[j]) {if(!dfs(j, 3 - color)) return false;}else if(st[j] == color) return false;}return true;
}int main(){int n, m;scanf("%d%d", &n, &m);memset(h, -1, sizeof h);while (m --){int a, b;scanf("%d%d", &a, &b);add(a, b), add(b,a); // 无向图,a->b, b->a}bool flag = true;for(int i = 1; i <= n; i ++){if(!st[i]){if(!dfs(i, 1)){flag = false;break;}}}if(flag) puts("Yes");else puts("No");return 0;
}作者:gyh
链接:https://www.acwing.com/solution/content/5281/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
匈牙利算法
二分图的最大匹配
给定一个二分图,其中左半部包含 n1n1 个点(编号 1∼n11∼n1),右半部包含 n2n2 个点(编号 1∼n21∼n2),二分图共包含 mm 条边。
数据保证任意一条边的两个端点都不可能在同一部分中。
请你求出二分图的最大匹配数。
二分图的匹配:给定一个二分图 GG,在 GG 的一个子图 MM 中,MM 的边集 {E}{E} 中的任意两条边都不依附于同一个顶点,则称 MM 是一个匹配。
二分图的最大匹配:所有匹配中包含边数最多的一组匹配被称为二分图的最大匹配,其边数即为最大匹配数。
输入格式
第一行包含三个整数 n1n1、 n2n2 和 mm。
接下来 mm 行,每行包含两个整数 uu 和 vv,表示左半部点集中的点 uu 和右半部点集中的点 vv 之间存在一条边。
输出格式
输出一个整数,表示二分图的最大匹配数。
数据范围
1≤n1,n2≤5001≤n1,n2≤500,
1≤u≤n11≤u≤n1,
1≤v≤n21≤v≤n2,
1≤m≤1051≤m≤105
输入样例:
2 2 4
1 1
1 2
2 1
2 2
输出样例:
2
#include<iostream>
#include<cstring>
using namespace std;
const int N = 510 , M = 100010;
int n1,n2,m;
int h[N],ne[M],e[M],idx;
bool st[N];
int match[N];void add(int a , int b)
{e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}void init()
{memset(h,-1,sizeof h);
}int find(int x)
{//遍历自己喜欢的女孩for(int i = h[x] ; i != -1 ;i = ne[i]){int j = e[i];if(!st[j])//如果在这一轮模拟匹配中,这个女孩尚未被预定{st[j] = true;//那x就预定这个女孩了//如果女孩j没有男朋友,或者她原来的男朋友能够预定其它喜欢的女孩。配对成功if(!match[j]||find(match[j])){match[j] = x;return true;}}}//自己中意的全部都被预定了。配对失败。return false;
}
int main()
{init();cin>>n1>>n2>>m;while(m--){int a,b;cin>>a>>b;add(a,b);}int res = 0;for(int i = 1; i <= n1 ;i ++){ //因为每次模拟匹配的预定情况都是不一样的所以每轮模拟都要初始化memset(st,false,sizeof st);if(find(i)) res++;} cout<<res<<endl;
}作者:Charles__
链接:https://www.acwing.com/solution/content/5334/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
搜索与图论模板题(必备)Day3相关推荐
- 数据结构模板题(必备)Day2
单链表 实现一个单链表,链表初始为空,支持三种操作: 向链表头插入一个数: 删除第 kk 个插入的数后面的数: 在第 kk 个插入的数后插入一个数. 现在要对该链表进行 MM 次操作,进行完所有操作后 ...
- ~~朴素dijkstra算法 (搜索与图论)(附模板题AcWing 849. Dijkstra求最短路 I)
模板 时间复杂是 O(n2+m), n表示点数,m 表示边数 int g[N][N]; // 存储每条边 int dist[N]; // 存储1号点到每个点的最短距离 bool st[N]; // 存 ...
- 图论-有向图的连通性模板题(hdu1296)(hdu1827)
1.强连通分量: 强连通分量可以理解为边数最少的情况下是一个环. 这里写了一个模板题用的是tarjan算法,当然还有其他算法. tarjan算法的关键其实还是对于num数组和low数组的使用 然后可以 ...
- 一起开心2020暑假训练第二周 图论(模板题)
比赛链接: 文章目录 A HDU 1285 一 B HDU 1863 起 C POJ 2387 开 D POJ 1502 心 E HDU 5922 图 F HDU 2112 论 A HDU 1285 ...
- 关于最近的感想以及贴些图论模板
春节后的集训不久前又开始辣~十天的寒假过得跟做梦一样,文化课的的作业匆匆赶完,在我们的认知中"选做=不做".年前是DP,什么线性DP.树上DP.斜率优化,(巴拉巴拉).原本就基础不 ...
- 图论模板,随缘不定期更新
图论算法模板,随缘不定期更新 搜索(更新于2021/1/11) DFS BFS 并查集(更新于2021/3/20---10:54) 概念及作用 原理及实现 完整代码 启发式合并 网络流 最大流 din ...
- 个人算法题精简导航整理(精炼汇总,含知识点、模板题、题单)
文章目录 前言 导航 注意事项 技巧类 自定义Pair 排序 N维数组转一维 位运算 状态压缩 算法基础 枚举 √ 指数型枚举 排列型枚举 组合型枚举 左右区间枚举 模拟 √ 日期天数问题:平年闰年情 ...
- CSP认证201509-4 高速公路[C++题解]:强连通分量、tarjan算法模板题
题目分析 来源:acwing 分析: 所求即为强连通分量的个数,然后计算每个强连通分量中点的个数,相加即可. 所谓强连通分量,它是一个子图,其中任意两点可以相互到达,并且再加一个点,就不能满足任意两点 ...
- 【转载】图论 500题——主要为hdu/poj/zoj
转自--http://blog.csdn.net/qwe20060514/article/details/8112550 =============================以下是最小生成树+并 ...
最新文章
- 阿里云弹性计算-图形工作站(公测)发布
- 最强大,最简洁的【禁止输入中文】
- ​Rust最受喜爱却少有人用,Python仅排第六,2021全球开发者调查报告出炉
- Java对象转xml报文和xml报文转Java对象帮助类
- 【C 语言】指针 与 数组 ( 指针 | 数组 | 指针运算 | 数组访问方式 | 字符串 | 指针数组 | 数组指针 | 多维数组 | 多维指针 | 数组参数 | 函数指针 | 复杂指针解读)
- 11月14号站立会议(从即日14号起到24号截至为final阶段工作期)
- ? php 为啥报错,如何解决js里面的php代码报错问题
- 剑指offer 56 - 1.数组中数字出现的次数
- 云小课|云数据库RDS实例连接失败了?送你7大妙招轻松应对
- docker -v 挂载文件_浅谈关于docker中数据卷的操作,附带案例
- vdcode C语言不能弹出运行窗口_C语言编程常见问题分析,以及错误解决办法!
- update关联其他表批量更新数据-跨数据库-跨服务器Update时关联表条件更新
- 通感一体化融合架构及关键技术
- pyton人值得拥有
- ORACLE临时表空间总结
- Please refer to E:\SSM\Distributed\edu-parent\edu-dao\target\surefire-reports for the individua
- 最新版校园招聘进大厂系列----------(3)字节篇 -----未完待续
- 爬虫 爬取百思不得姐网站
- Radare2 框架介绍及使用
- 微软与索尼赋能计划已启动,旨共同创造新技术生态系统