搜索
八数码
题目链接:https://www.acwing.com/problem/content/847/
这道题中比较难的地方在于状态的表示
我们将其作为一个string表示状态存入map中即可
import java.util.*;
/**
* @title: Main
* @Author CheerJia
* @Date: 2021/3/6 10:23
* @Version 1.0
*/
public class Main {
static Scanner sc = new Scanner(System.in);
public static void main(String[] args) {
String start = "";
for (int i = 0; i < 9; i++) {
start += sc.next();
}
// System.out.println(start);
System.out.println(bfs(start));
}
public static int bfs(String start){
// 终点
String end = "12345678x";
// 四个方向
int[] dx = {-1,0,1,0}, dy = {0,1,0,-1};
// 队列
Queue<String> q = new LinkedList<>();
// 状态与步数
Map<String,Integer> dis = new HashMap();
dis.put(start,0);
q.offer(start);
// 标准bfs
while(!q.isEmpty()){
String t = q.poll();
int k = t.indexOf('x'), distance = dis.get(t);
if(t.equals(end)) return distance;
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){
String cur = swap(t, k, a * 3 + b);
if(!dis.containsKey(cur)) {
dis.put(cur, distance + 1);
q.offer(cur);
}
}
}
}
return -1;
}
private static String swap(String t, int k, int j) {
String res = "";
for (int i = 0; i < t.length(); i++) {
if(i == k) res += t.charAt(j);
else if(i == j) res += t.charAt(k);
else res += t.charAt(i);
}
return res;
}
}
树的重心
重心定义:重心是指树中的一个结点,如果将这个点删除后,剩余各个连通块中点数的最大值最小,那么这个节点被称为树的重心。
题目链接:https://www.acwing.com/problem/content/848/
利用dfs遍历图,每次返回该结点所有子树的结点树,如果当删除该结点时,则可求出每个子树的结点数目(每个连通块),而其他的连通块则为(n - dfs(cur))总数减去当前结点树,最后ans取所有连通块中最大值的最小值
详情见代码:
import java.util.*;
/**
* @title: Main
* @Author CheerJia
* @Date: 2021/3/6 10:23
* @Version 1.0
*/
public class Main {
static Scanner sc = new Scanner(System.in);
static final int N = (int)1e5 + 10;
static int[] e = new int[N * 2], ne = new int[N * 2], h = new int[N];
static int idx = 0, ans = N, n;
static boolean[] vis = new boolean[N];
// 数组表示单链表头插
static void add(int a, int b){
e[idx] = b;
ne[idx] = h[a];
h[a] = idx ++;
}
static int dfs(int u){
// sum 表示当前结点有多少个, 加上自身,故初始值为1, res 表示各个子树结点数的最大值(也就是去掉当前结点各个连通块数量的最大值)
int sum = 1, res = 0;
// 表示走过
vis[u] = true;
// 循环邻阶矩阵 h[u] --> node ---> node
for(int i = h[u]; i != -1; i = ne[i]){
if(!vis[e[i]]){
// 子树e[i] 的总结点数 为s
int s = dfs(e[i]);
// 各个子连通块的数量
res = Math.max(res, s);
// 将各个子树结点 相加 再加上本身1 则表示当前总节点数
sum += s;
}
}
// 去掉u结点后, 不仅有各个子连通块s, 还有一个整体大的连通块 就是 n - sum
res = Math.max(res, n - sum);
// ans 取各个最大连通块的最小值
ans = Math.min(res, ans);
return sum;
}
public static void main(String[] args) {
Arrays.fill(h,-1);
n = sc.nextInt();
for (int i = 0; i < n - 1; i++) {
int a = sc.nextInt(), b = sc.nextInt();
// 无向图 做有向图
add(a,b);
add(b,a);
}
dfs(1);
System.out.println(ans);
}
}
图中点的层次
题目链接:https://www.acwing.com/problem/content/849/
题目描述:从图中找到从一号点到n号点的最短距离是多少;
直接bfs可以求出
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
/**
* @title: Main
* @Author CheerJia
* @Date: 2021/3/8 13:43
* @Version 1.0
*/
public class Main {
static Scanner sc = new Scanner(System.in);
static final int N = (int)1e5 + 10;
static int[] e = new int[N], ne = new int[N], h = new int[N];
static int idx = 0,n,m;
static void add(int a,int b){
e[idx] = b;
ne[idx] = h[a];
h[a] = idx ++;
}
public static void main(String[] args) {
Arrays.fill(h, -1);
n = sc.nextInt(); m = sc.nextInt();
for(int i = 0; i < m; i ++){
int a = sc.nextInt(), b = sc.nextInt();
add(a,b);
}
System.out.println(bfs());
}
private static int bfs() {
Queue<int[]> q = new LinkedList<>();
q.offer(new int[]{1,0});
while(!q.isEmpty()){
int[] arr = q.poll();
Integer cur = arr[0], dis = arr[1];
if(cur == n) return dis;
for(int i = h[cur]; i != -1; i = ne[i]){
q.offer(new int[]{e[i],dis + 1});
}
}
return -1;
}
}
有向图的拓扑序列
题目链接:https://www.acwing.com/problem/content/850/
题目描述:找一个有向图的拓扑序列
拓扑排序模板
import java.util.*;
/**
* @title: Main
* @Author CheerJia
* @Date: 2021/3/8 16:57
* @Version 1.0
*/
public class Main {
static Scanner sc = new Scanner(System.in);
static final int N = (int)1e5 + 10;
public static void main(String[] args) {
int n = sc.nextInt(), m = sc.nextInt();
List[] list = new ArrayList[n + 1];
int[] degree = new int[N];
for (int i = 0; i <= n; i++) {
list[i] = new ArrayList<Integer>();
}
for(int i = 0; i < m; i ++){
int a = sc.nextInt(), b = sc.nextInt();
list[a].add(b);
degree[b]++;
}
List<Integer> res = new ArrayList<>();
Queue<Integer> q = new LinkedList<>();
boolean[] vis = new boolean[n + 1];
for (int i = 1; i <= n; i++) {
if(degree[i] == 0){
res.add(i);
q.offer(i);
vis[i] = true;
}
}
while (!q.isEmpty()){
int node = q.poll();
for(Object object : list[node]){
int cur = (Integer)object;
degree[cur]--;
if(degree[cur] == 0){
if(!vis[cur]){
vis[cur] = true;
q.offer(cur);
res.add(cur);
}else {
System.out.println("-1");
return ;
}
}
}
}
if (res.isEmpty() || res.size() != n) System.out.println("-1");
else {
for (int ans : res) System.out.print(ans + " ");
}
}
}
最短路
Dijkstra求最短路 I
题目链接:https://www.acwing.com/problem/content/851/
求最短路径
Dijkstra算法:
分成两个集合,首先只要起点在集合中,将离该集合最近的一个点加入集合,并且更新dis的距离即可,直到所有点都加入集合
import java.util.Arrays;
import java.util.Scanner;
/**
* @title: Main
* @Author CheerJia
* @Date: 2021/3/8 22:58
* @Version 1.0
*/
public class Main {
static Scanner sc = new Scanner(System.in);
static final int N = (int)1e5 + 10, inf = 0x3f3f3f3f;
static int[][] g;
static boolean[] vis = new boolean[505];
static int[] dis = new int[505];
static void init(int len){
Arrays.fill(dis,inf);
for(int i = 0; i <= len; i ++){
for(int j = 0; j <= len; j ++){
g[i][j] = inf;
}
}
}
public static void main(String[] args) {
int n = sc.nextInt(), m = sc.nextInt();
g = new int[n + 1][n + 1];
init(n);
for(int i = 0; i < m; i ++){
int a = sc.nextInt(), b = sc.nextInt(), c = sc.nextInt();
g[a][b] = Math.min(g[a][b], c);
}
// 初始化dis
for(int i = 2; i <= n; i ++){
if(g[1][i] != inf){
dis[i] = g[1][i];
}
}
System.out.println(Dijkstra(n));
}
static int Dijkstra(int n){
vis[1] = true;
dis[1] = 0;
for(int i = 0; i < n - 1; i ++){
// node 代表点, min代表最短的距离,也可以直接用dis[node]代替min
int node = 1, min = inf;
for(int j = 1; j <= n; j++){
if(!vis[j] && dis[j] < min){
min = dis[j];
node = j;
}
}
vis[node] = true;
for(int j = 1; j <= n; j ++){
if(dis[j] > min + g[node][j]){
dis[j] = min + g[node][j];
}
}
}
if(dis[n] == inf) return -1;
return dis[n];
}
}
Dijkstra求最短路 II
堆优化版Dijkstra算法;
他人题解:https://www.acwing.com/solution/content/6291/ (我是懒狗)
import java.util.Arrays;
import java.util.PriorityQueue;
import java.util.Scanner;
/**
* @title: Main
* @Author CheerJia
* @Date: 2021/3/8 22:58
* @Version 1.0
*/
public class Main {
static Scanner sc = new Scanner(System.in);
static final int N = (int)2e5 + 10, inf = 0x3f3f3f3f;
static int idx = 0;
static boolean[] vis = new boolean[N];
static int[] dis = new int[N],h = new int[N], ne = new int[N], w = new int[N], e = new int[N];
static void add(int a, int b,int c){
e[idx] = b;
ne[idx] = h[a];
w[idx] = c;
h[a] = idx ++;
}
static void init(){
Arrays.fill(dis,inf);
Arrays.fill(h,-1);
}
public static void main(String[] args) {
int n = sc.nextInt(), m = sc.nextInt();
init();
for(int i = 0; i < m; i ++){
int a = sc.nextInt(), b = sc.nextInt(), c = sc.nextInt();
add(a,b,c);
}
Dijkstra(n);
System.out.println(Dijkstra(n));
}
static int Dijkstra(int n){
dis[1] = 0;
PriorityQueue<int[]> q = new PriorityQueue<int[]>((a,b)-> a[0] - b[0]);
int[] tt = new int[]{0,1};
q.offer(tt);
while(q.size() > 0){
int[] t = q.poll();
int ver = t[1], distance = t[0];
if(vis[ver]) continue;
vis[ver] = true;
for(int i = h[ver]; i != -1; i = ne[i]){
int j = e[i];
if(dis[j] > distance + w[i]){
dis[j] = distance + w[i];
}
q.offer(new int[]{dis[j],j});
}
}
if(dis[n] == inf) return -1;
return dis[n];
}
}
有边数限制的最短路
题意:边可能为负,并且要在固定边数内,找到最短路径
题目:https://www.acwing.com/problem/content/855/
大佬题解:https://www.acwing.com/solution/content/6320/
import java.util.Arrays;
import java.util.PriorityQueue;
import java.util.Scanner;
/**
* @title: Main
* @Author CheerJia
* @Date: 2021/3/8 22:58
* @Version 1.0
*/
public class Main {
static Scanner sc = new Scanner(System.in);
static final int N = 10010 , inf = 0x3f3f3f3f;
static int n,m,k;
static int[] dis = new int[N], backup = new int[N];
static Edge[] edges = new Edge[N];
public static void main(String[] args) {
n = sc.nextInt(); m = sc.nextInt(); k = sc.nextInt();
for(int i = 0; i < m; i ++){
int a = sc.nextInt(), b = sc.nextInt(), w = sc.nextInt();
edges[i] = new Edge(a,b,w);
}
int res = Bellman_ford();
// 有负边,可能最后的值小于inf
if(res > inf / 2) System.out.println("impossible");
else System.out.println(res);
}
static int Bellman_ford(){
Arrays.fill(dis,inf);
dis[1] = 0;
for(int i = 0; i < k; i ++){
System.arraycopy(dis,0,backup,0,n + 1);
for(int j = 0; j < m; j ++){
int a = edges[j].a, b = edges[j].b, w = edges[j].w;
dis[b] = Math.min(dis[b],backup[a] + w);
}
}
return dis[n];
}
static class Edge{
int a,b,w;
public Edge(int a, int b, int w) {
this.a = a;
this.b = b;
this.w = w;
}
}
}
spfa求最短路
题意:有负权边,求最短路径
题目链接:https://www.acwing.com/problem/content/description/853/
为什么Dijkstra不能处理负边
spfa基本为bellman-ford算法的优化, bellman-ford是拿所有边进行松弛,可以处理有负权边是因为 bellman-ford拿所有边松驰并且正确的顺序就是从第一步能走的边到n-1步能走的边进行松弛,每条边都用来松弛过;而Dijkstra把没在集合内的点加入路径,如果边的长度是负的,就有可能产生更小的d[x]!而Dijkstra根本没有不会考虑 “把没在集合内的点加入路径”这种情况。
简而言之:
- bellman-ford算法:会将所有到改点的边都进行松弛
- Dijkstra算法:当第一次认为到x点为最短时,就永远将其认为最短,故不能处理负边
如下图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6mfZxzlN-1618468009163)(…/…/img/image-20210328080800452.png)]
Dijkstra 第一步加入2号点,然后加入4号点,此时dis[4] = 2 + 2 = 4; (路径为1 --> 2 --> 4)但是 (路径 1 --> 3 --> 4) 为3 按理说dis[4] = 3,但是Dijkstra 不会再更新,而bellman-ford 会;
spfa是什么?
spfa则是当bellman-ford算法能进行松弛时,才松弛,而不是像其暴力循环。spfa对其的优化:将松弛成功的点加入队列(只有该点变化过,才能拿来松弛其他点)
import java.util.*;
/**
* @title: Main
* @Author CheerJia
* @Date: 2021/3/8 22:58
* @Version 1.0
*/
public class Main {
static Scanner sc = new Scanner(System.in);
static final int N = (int)1e5 + 10 , inf = 0x3f3f3f3f;
static int n,m,idx = 0;
static int[] dis = new int[N], e = new int[N], ne = new int[N], w = new int[N], h = new int[2 * N];
static boolean[] vis = 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 void main(String[] args) {
Arrays.fill(h,-1);
n = sc.nextInt(); m = sc.nextInt();
for(int i = 0; i < m; i ++){
int a = sc.nextInt(), b = sc.nextInt(), w = sc.nextInt();
add(a,b,w);
}
int res = spfa();
// 有负边,可能最后的值小于inf
if(res > inf / 2) System.out.println("impossible");
else System.out.println(res);
}
static int spfa(){
Arrays.fill(dis,inf);
dis[1] = 0;
Queue<Integer> q = new LinkedList<>();
q.offer(1);
vis[1] = true;
while(!q.isEmpty()) {
int t = q.poll();
vis[t] = false;
for(int i = h[t]; i != -1; i = ne[i]){
int j = e[i];
if(dis[j] > dis[t] + w[i]){
dis[j] = dis[t] + w[i];
if(!vis[j]) {
q.offer(j);
vis[j] = true;
}
}
}
}
return dis[n];
}
}
spfa判断负环
题意:判断图中是否存在负权回路。
题目链接:https://www.acwing.com/problem/content/854/
利用cnt[] 记录路径步数,当cnt[x] >= n时,代表使用了n条边,有n+1个结点,则代表有重复结点,故有负权回路
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
/**
* @title: Main
* @Author CheerJia
* @Date: 2021/3/8 22:58
* @Version 1.0
*/
public class Main {
static Scanner sc = new Scanner(System.in);
static final int N = (int)1e5 + 10 , inf = 0x3f3f3f3f;
static int n,m,idx = 0;
static int[] dis = new int[N], e = new int[N], ne = new int[N], w = new int[N], h = new int[2 * N], cnt = new int[N];
static boolean[] vis = new boolean[N];
public static void add(int a, int b,int c){
e[idx] = b;
ne[idx] = h[a];
w[idx] = c;
h[a] = idx ++;
}
public static void main(String[] args) {
Arrays.fill(h,-1);
n = sc.nextInt(); m = sc.nextInt();
for(int i = 0; i < m; i ++){
int a = sc.nextInt(), b = sc.nextInt(), w = sc.nextInt();
add(a,b,w);
}
boolean res = spfa();
// 有负边,可能最后的值小于inf
if(res) System.out.println("Yes");
else System.out.println("No");
}
static boolean spfa(){
Arrays.fill(dis,inf);
dis[1] = 0;
Queue<Integer> q = new LinkedList<>();
for (int i = 1; i <= n; i++) {
q.offer(i);
vis[i] = true;
}
while(!q.isEmpty()){
int t = q.poll();
vis[t] = false;
for(int i = h[t]; i != -1; i = ne[i]){
int j = e[i];
if(dis[j] > dis[t] + w[i]) {
dis[j] = dis[t] + w[i];
cnt[j] = cnt[t] + 1;
if(cnt[j] >= n) {
return true;
}
if(!vis[j]) {
vis[j] = true;
q.offer(j);
}
}
}
}
return false;
}
}
Floyd求最短路
题目链接:https://www.acwing.com/problem/content/856/
本质为一个dp;
-
dis[i][j]
代表从i到j的距离; -
f[i, j, k]表示从i走到j的路径上除i和j点外只经过1到k的点的所有路径的最短距离。那么f[i, j, k] = min(f[i, j, k - 1), f[i, k, k - 1] + f[k, j, k - 1]。因此在计算第k层的f[i, j]的时候必须先将第k - 1层的所有状态计算出来,所以需要把k放在最外层。
#include<iostream>
using namespace std;
const int N = 210, INF = 1e9;
int dis[N][N], n, m, k;
void forld(){
for(int k = 1; k <= n; k ++){
for(int i = 1; i <= n; i ++){
for(int j = 1; j <= n; j ++){
dis[i][j] = min(dis[i][j], dis[i][k] + dis[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) dis[i][j] = 0;
else dis[i][j] = INF;
}
}
for(int i = 0; i < m; i ++) {
int a, b, c;
cin >> a >> b >> c;
if(dis[a][b] > c) dis[a][b] = c;
}
forld();
while(k --){
int a, b;
cin >> a >> b;
if(dis[a][b] > INF / 2) puts("impossible");
else cout << dis[a][b] << endl;
}
return 0;
}
最短路简要总结
Dijkstra
一般适用稠密图: 用邻接矩阵
核心:不断选择离源点集合近的点,加入集合,并拿该点加入源点集合;
步骤:分为两个集合,S表示源点经过的集合,U被选择的集合
- for 1 to n - 1 循环 n - 1次,选择n-1个点加入S集合,初始时源点已经加入
- for 1 to n 循环,找到一个U集合中离S集合近的点t加入S集合
- for 1 to n循环,拿点t更新其他距离
Dijkstra堆优化
一般适用稀疏图:邻接表
核心:利用优先队列priority_queue<Integer,Integer>每次将离S集合近的点取出,来更新
步骤:
- 将源点加入S集合和优先队列 q.offer(距离,点)
- 利用堆顶的点poll()出,对其进行更新
Bellman_ford
穷举所有边,故一般用数组结构体存储:可以存在负权回路
static class Edge{
int a,b,w;
public Edge(int a, int b, int w) {
this.a = a;
this.b = b;
this.w = w;
}
}
Edge edge[N];
步骤:
- for 1 to k(k为边数限制 一般为n - 1 如果k == n 还可以继续松弛代表有负权回路)
- for 1 to m(m 所有边) 在其中进行松弛
spfa
基本为bellman_ford优化:一般用临界表,bellman_ford将所有边进行松弛,而spfa只将能松弛的才进行松弛
步骤:利用队列
- 将源点加入队列q.offer(1)
- 取出队首q.poll(),将其进行松弛,松弛成功的点继续加入队列
Floyd
求的是多源最短路径,本质为一个dp,f[i,j,k]表示只能经过1~k个点从i到j的最短路径f[i, j, k] = min(f[i, j, k - 1), f[i, k, k - 1] + f[k, j, k - 1]。因此在计算第k层的f[i, j]的时候必须先将第k - 1层的所有状态计算出来,所以需要把k放在最外层。
步骤:
for(int k = 1; k <= n; k ++){
for(int i = 1; i <= n; i ++){
for(int j = 1; j <= n; j ++){
dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
}
}
}
最小生成树
Prim算法
题目链接:https://www.acwing.com/problem/content/860/
Prim和Dijkstra很相似,只不过一个dis代表的是源点到其他点的距离,一个是集合到其他点的距离
步骤:
- 找到未在集合点的最近点
- 拿该点更新其他点到集合的距离
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 510, INF = 0x3f3f3f3f;
int n, m, g[N][N], dis[N], vis[N];
int prime(){
memset(dis, INF, sizeof dis);
int res = 0;
for(int i = 0; i < n; i ++){
// 第一个集合之外的点直接赋值给t
int t = -1;
for(int j = 1; j <= n; j ++){
if(!vis[j] && (t == -1 || dis[t] > dis[j]))
t = j;
}
vis[t] = true;
// 如果集合为Null则距离为0
if(i) res += dis[t];
if(i && dis[t] == INF) return INF;
for(int j = 1; j <= n; j ++) dis[j] = min(dis[j], g[t][j]);
}
return res;
}
int main(){
memset(g, INF, sizeof g);
cin >> n >> m;
for(int i = 0; i < m; i ++){
int a, b, c;
cin >> a >> b >> c;
g[a][b] = g[b][a] = min(g[a][b],c);
}
int res = prime();
if(res == INF) puts("impossible");
else cout << res << endl;
return 0;
}
Kruskal算法
题目链接:https://www.acwing.com/problem/content/861/
核心:每次拿集合外最短的边加入集合,如果该边的点已经在集合则跳过;
步骤:
- 将所有的边存入Edge[N],并按照边长排序
- 枚举所有的边,判断边的两个点在不在一个集合中,在则跳过,不在则加入并且res += 路径;
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100005, INF = 0x3f3f3f3f;
int n,m, idx = 0;
int f[N];
struct Edge{
int a,b;
int c;
bool operator < (const Edge &W) const{
return c < W.c;
}
}e[2 * N];
int Find(int x){
return f[x] == x ? x : f[x] = Find(f[x]);
}
int Kruskal(){
int res = 0, cnt = 0;
for(int i = 0; i < m; i ++){
int r1 = Find(e[i].a), r2 = Find(e[i].b);
if(r1 != r2){
res += e[i].c;
f[r1] = r2;
cnt ++;
}
}
// int r = Find(1);
// for(int i = 2; i <= n; i ++){
// if(r != Find(i)) return INF;
// }
if(cnt != n - 1) return INF;
return res;
}
int main(){
cin >> n >> m;
for(int i = 0; i < m; i ++){
int a, b, c;
cin >> a >> b >> c;
e[i].a = a; e[i].b = b; e[i].c = c;
}
sort(e, e + m);
for(int i = 1; i <= n; i ++) f[i] = i;
int res = Kruskal();
if(res == INF) puts("impossible");
else cout << res << endl;
return 0;
}
染色
二分图
题目链接:https://www.acwing.com/problem/content/862/
-
二分图:当且仅当图中不含有奇数环,两个集合内部的内部没有边
-
奇数环:由奇数条边形成的一个环
步骤:使用dfs或者bfs遍历判断
- 从顶点开始用dfs或者bfs染色
- 可以染色时就染成与上一个不同的颜色
- 不能染色时判断与上一个颜色是不是相同
- 相同则返回false也代表有奇数环
- 不相同则跳过
- 成功染色完毕则
return true
import java.util.Arrays;
import java.util.Scanner;
/**
* @title: Main
* @Author CheerJia
* @Date: 2021/3/8 22:58
* @Version 1.0
*/
public class Main {
static Scanner sc = new Scanner(System.in);
static final int N = (int)1e5 + 100 , inf = 0x3f3f3f3f;
static int n,m,k, idx = 0;
static int[] e = new int[2 * N], ne = new int[2 * N], h = new int[2 * N], color = new int[2 * N];
static void add(int a, int b){
e[idx] = b;
ne[idx] = h[a];
h[a] = idx ++;
}
static boolean dfs(int u, int c){
color[u] = c;
for(int i = h[u]; i != -1; i = ne[i]){
int j = e[i];
if(color[j] == 0){
if(!dfs(j, 3 - c)) return false;
}else if(color[j] == c) {
return false;
}
}
return true;
}
public static void main(String[] args) {
Arrays.fill(h,-1);
n = sc.nextInt(); m = sc.nextInt();
for(int i = 0; i < n; i ++){
int a = sc.nextInt(), b = sc.nextInt();
add(a,b); add(b,a);
}
boolean f = true;
for(int i = 1; i <= n; i ++){
if(color[i] == 0){
if(!dfs(i,1)) {
f = false;
break;
}
}
}
if(f) System.out.println("Yes");
else System.out.println("No");
}
}
二分图的最大匹配
题目链接:https://www.acwing.com/problem/content/863/
步骤:分为左集合和右集合
- 左集合点u中通过边找右集合的点v,找到则标记v属于u
- 如果左集合u找到v已经有主人了,则让v的主人尝试是否有其他仆人,如果有则更改为其他仆人,并且标记v属于新主人u
- 输出共多少对主仆
import java.util.Arrays;
import java.util.Scanner;
/**
* @title: Main
* @Author CheerJia
* @Date: 2021/3/8 22:58
* @Version 1.0
*/
public class Main {
static Scanner sc = new Scanner(System.in);
static final int N = (int)1e5 + 100;
static int n1,n2,m, idx = 0;
static int[] e = new int[2 * N], ne = new int[2 * N], h = new int[2 * N], match = new int[2 * N];
static boolean[] st = new boolean[N];
static void add(int a, int b){
e[idx] = b;
ne[idx] = h[a];
h[a] = idx ++;
}
static boolean find(int u){
for(int i = h[u]; i != -1; i = ne[i]){
int j = e[i];
if(!st[j]) {
st[j] = true;
if(match[j] == 0 || find(match[j])){
match[j] = u;
return true;
}
}
}
return false;
}
public static void main(String[] args) {
Arrays.fill(h,-1);
n1 = sc.nextInt(); n2 = sc.nextInt(); m = sc.nextInt();
for(int i = 0; i < m; i ++){
int a = sc.nextInt(), b = sc.nextInt();
add(a,b);
}
int res = 0;
for(int i = 1; i <= n1; i ++){
Arrays.fill(st,false);
if(find(i)) res ++;
}
System.out.println(res);
}
}
st数组的作用
算法过程:当左边u1找到右边v1时,此时u2找到v1,让u1重新找;
如果没有st数组的话 ----> find(match[j]) 则会一直进入并且死循环
如果st不在每个结点开始时就重置的话 ----> u2 到不了v1 也就不能让 u1重新找