蓝桥杯java算法学习(三)

注意:以下部分内容摘自Acwing,仅用于个人学习记录,不做任何商业用途。

(1)深搜dfs

例子:n皇后问题

核心思路:深度优先遍历

函数名:void dfs(int r): 深度优先遍历函数。参数r:从第r行开始放棋子,处理第r行。

递归结束判定:见代码,当 r == n的时候,说明应该处理第 n行了,也代表第 0~n-1行放好棋子,也就是整个棋盘放好了棋子,也就是得到了一种解,也就是递归结束。

第r行,第i列能不能放棋子:用数组dg udg cor 分别表示:点对应的两个斜线以及列上是否有皇后。
dg[i + r] 表示 r行i列处,所在的对角线上有没有棋子,udg[n - i + r]表示 r行i列处,所在的反对角线上有没有棋子,cor[i]表示第i列上有没有棋子。如果 r行i列的对角线,反对角线上都没有棋子,即!cor[i] && !dg[i + r] && !udg[n - i + r]为真,则代表 r行i列处可以放棋子。

作者:Hasity
链接:https://www.acwing.com/solution/content/30231/
来源:AcWing
 

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {
	
	private static int N=11;
	private static char[][] q=new char[N][N];  //存储棋盘
	//点对应的两个斜线以及列上是否有皇后
	private static int[] dg=new int[N*2];
	private static int[] udg=new int[N*2];
	private static int[] cor=new int[N*2];
	private static int n;
	
	//dfs函数
	public static void dfs(int u) {
		//放满了棋盘,输出棋盘
		if(u==n) {
			for(int i=0;i<n;i++)
			{
				System.out.println(q[i]);
			}
			System.out.println();
			return;
		}
		
		//第 u 行,第 i 列 是否放皇后
		for(int i=0;i<n;i++) {
			//不冲突,放皇后
			if(dg[i+u]==0&&udg[n-i+u]==0&&cor[i]==0) {
				q[u][i]='Q';
				//对应的 列, 斜线 状态改变
				dg[i+u]=1;
				udg[n-i+u]=1;
				cor[i]=1;
				dfs(u + 1);//处理下一行
				//恢复现场
				dg[i+u]=0;
				udg[n-i+u]=0;
				cor[i]=0;
				q[u][i]='.';
			}
		}
	}
	
	
	public static void main(String[] args) throws IOException {
		BufferedReader br =new BufferedReader(new InputStreamReader(System.in));
		n=Integer.parseInt(br.readLine());
		for(int i=0;i<n;i++)
			for(int j=0;j<n;j++)
				q[i][j]='.';
		dfs(0);
	}
	
}

(2)宽搜bfs

在求最短路问题时,只有当边权重都为1的时候才能用bfs来做。

求解走迷宫问题:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.Queue;

//存储路径
class Pair {
    int x;
    int y;

    public Pair(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

public class Main {
	
	private static int N=110;
	private static int[][] g=new int[N][N];  //存储地图
	private static int[][] f=new int[N][N];  //存储距离
	private static int n,m;
	
	//dfs函数
	public static void bfs(int a,int b) {
		int[] dx = {0, 1, 0, -1};
		int[] dy = {-1, 0, 1, 0};
	    Queue<Pair> q=new LinkedList<Pair>();
	    q.offer(new Pair(a, b));
	    f[0][0] = 0;
	    while (!q.isEmpty()) {
	    	Pair pair = q.poll();
	    	g[pair.x][pair.y]=1;
	    	
	    	for(int i = 0; i < 4; i++)//往四个方向走
	    	{
	    		int x1=pair.x+dx[i],y1=pair.y+dy[i];
	    		//如果还没走过
	    		if(x1 > 0 && x1 <= n && y1 > 0 && y1 <= m&&g[x1][y1]==0) {
	    			 g[x1][y1] = 1;
	    			 f[x1][y1] = f[pair.x][pair.y]+1;
	    			//这个点放入队列,用来走到和它相邻的点。
	    			 q.offer(new Pair(x1, y1));
	    		}
	    	}
	    }
	    System.out.print(f[n][m]);
	}
	
	
	public static void main(String[] args) throws IOException {
		BufferedReader br =new BufferedReader(new InputStreamReader(System.in));
		String[] nums = br.readLine().split(" ");
        n = Integer.parseInt(nums[0]);
        m = Integer.parseInt(nums[1]);

      //迷宫map
        for(int i = 1; i <= n; i++) {
            String[] inputs = br.readLine().split(" ");
            for (int j = 1; j <= m; j++) {
                g[i][j] = Integer.parseInt(inputs[j-1]);
            }
        }
        
        bfs(1, 1);
	}
	
}

例二:八数码问题https://www.acwing.com/problem/content/847/



import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Queue;

public class Main {
	//结束状态
	static String endString="12345678x";
	//交换函数
	static void swap(char[] c,int a,int b) {
		char x=c[a];
		c[a]=c[b];
		c[b]=x;
	}
	public static int bfs(String start) {
		//开一个队列,存储所有状态
		Queue<String> queue=new LinkedList<>();
		//键值对存储所有状态和步骤数
		HashMap<String, Integer>map=new HashMap<String,Integer>();
		queue.offer(start);
		map.put(start,0);
		int[] dx= {0,0,1,-1};
		int[] dy= {1,-1,0,0};
		
		while(!queue.isEmpty()) {
			String string=queue.poll();
			//如果是最终状态就返回
			if(string.equals(endString))return map.get(string);
			//找出x的坐标
			int loca=string.indexOf('x');
			int x=loca/3,y=loca%3;
			for(int i=0;i<4;i++) {//每个状态都有四次变化
				int a=dx[i]+x,b=dy[i]+y;
				if(a>=0&&a<3&&b>=0&&b<3) {
					char arr[]=string.toCharArray();//字符串里面不能交换所以就到字符数组里,不直接修改t(以便后续的次数存储直接+1)
                    swap(arr,loca,a*3+b);//交换值&变状态(因为前面是一维存储字符串,所以二维坐标转一维下标)
				    String string2=new String(arr);
                    if(map.get(string2)==null) {
                    	queue.offer(string2);
                    	map.put(string2, map.get(string)+1);
                    }
				}
				
			}
		}
		
		return -1;
	}
	public static void main(String[] args) throws IOException {
		BufferedReader bReader=new BufferedReader(new InputStreamReader(System.in));
		String[] strings=bReader.readLine().split(" ");
		String start="";//因为输入问题所以不能直接给一个字符串
        for(int i=0;i<strings.length;i++){
            start+=strings[i];
        }
		System.out.print(bfs(start));
	}
    
}

(3)树的深搜dfs和宽搜bfs

        邻接表

int h[N], e[N * 2], ne[N * 2], idx;

void add(int a, int b) {
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

        深搜模板dfs

// 需要标记数组st[N],  遍历节点的每个相邻的便
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);
        }
    }
}

ACwing846:树的重心

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;

public class Main {
static int N=(int) (1e5+10);
static int[] h=new int[N];//头结点组
static int[] e=new int[N*2];
static int[] ne=new int[N*2];
static boolean[] st=new boolean[N];
static int idx=0;
static int ans = N; //表示重心的所有的子树中,最大的子树的结点数目
static int n;
public static void add(int a,int b) {
    e[idx]=b;
    ne[idx]=h[a];
    h[a]=idx++;
}
static public int no=0; 
//返回以u为根的子树中节点的个数,包括u节点
public static int dfs(int u) {
	int res=0;//存储 删掉某个节点之后,最大的连通子图节点数
	st[u]=true;
	int sum = 1; //存储 以u为根的树 的节点数, 包括u,所以初始化为1 就是包括u的意思
	
	//访问u的每个子结点
	for(int i=h[u];i!=-1;i=ne[i]) {
		int j=e[i];
		if(!st[j]) {
			int x=dfs(j);
			res=Math.max(res, x); // 记录最大联通子图的节点数
			sum+=x;//以j为根的树 的节点数
		}
	}
	res=Math.max(res, n-sum);
	ans=Math.min(ans,res);
	return sum;
}
    public static void main(String [] args) throws  IOException {
    	BufferedReader bReader=new BufferedReader(new InputStreamReader(System.in));
    	n=Integer.parseInt(bReader.readLine());
    	Arrays.fill(h,1,n+1,-1); 
    	String[] strings;
    	for(int g=0;g<n-1;g++) {
    	    strings=bReader.readLine().split(" ");
    		int a=Integer.parseInt(strings[0]);
    		int b=Integer.parseInt(strings[1]);
    		add(a, b);
    		add(b, a);
    	}
    	dfs(1);

    	System.out.println(ans);
    }
}

图的宽搜略,较简单。 

(4)拓扑排序

https://www.acwing.com/solution/content/103954/

(5)最短路算法

实现思路如下。

(一)朴素Dijkstra算法(适合无负权边稠密图m~n^2)

算法思路:https://www.acwing.com/solution/content/38318/

java实现:

import java.util.Arrays;
import java.util.Scanner;

/*
    给定一个n个点m条边的 有向图 ,图中可能存在重边和自环,所有边权均为正值。

    请你求出1号点到n号点的最短距离,如果无法从1号点走到n号点,则输出-1。

    输入格式
    第一行包含整数n和m。

    接下来m行每行包含三个整数x,y,z,表示存在一条从点x到点y的有向边,边长为z。

    输出格式
    输出一个整数,表示1号点到n号点的最短距离。

    如果路径不存在,则输出-1。

    数据范围
    1≤n≤500,
    1≤m≤10^5,
    图中涉及边长均不超过10000。

    输入样例:
    3 3
    1 2 2
    2 3 1
    1 3 4
    输出样例:
    3
 */
public class Main {
    //从这里看,边是比较多的,所有可以用邻接矩阵存数据

    static int N = 510;
    static int m, n;
    static int[][] g = new int[N][N];
    static int[] dist = new int[N];
    static boolean[] st = new boolean[N];

    //注意:有向图和无向图相比,无向图可以看出有向图
    //如果有重边,保留距离最短的那条边
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt(); m = sc.nextInt();

        for (int i = 1; i <= n; i++) Arrays.fill(g[i], 0x3f3f); //权值不超过10000
        while (m-- > 0) {
            int a = sc.nextInt(), b = sc.nextInt(), c = sc.nextInt();
            g[a][b] = Math.min(g[a][b], c);
        }

        // 表示1号点到n号点的最短距离
        System.out.println(dijkstra());
    }

    private static int dijkstra() {
        Arrays.fill(dist, 0x3f3f);

        dist[1] = 0; //把自己的距离设置为 0

        //遍历一遍 找到一个最小的点,加入到集合中,这里加入到集合里,是通过st来做

        //迭代n次,n次迭代后获得最终结果集
        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;
                }
            } //循环后找到了最短的点,为 t

            st[t] = true; // t 加进结果集中

            //最后拿 t 更新一下结果集
            for (int j = 1; j <= n; j++) dist[j] = Math.min(dist[j], dist[t] + g[t][j]);
        }

        if (dist[n] == 0x3f3f) return -1;
        else return dist[n];
    }
}

作者:lkm
链接:https://www.acwing.com/solution/content/19850/
来源:AcWing

(二)堆优化版Dijkstra算法(适合无负权边稀疏图n~m)

import java.io.*;
import java.lang.*;
import java.util.*;

class Main{
    static int n = 0, m = 0, N = 1000010;
    static PriorityQueue<int[]> q = new PriorityQueue<>((a,b)->{return a[1] - b[1];});//堆
    static int[] dist = new int[N];//距离数组
    static boolean[] f = new boolean[N];//标记数组
    static int[] h = new int[N], ne = new int[N], e = new int[N], w = new int[N];//邻接表
    static int idx = 1;
    static int Dijkstra(){//类似广搜的过程
        Arrays.fill(dist, 0x3f3f3f3f);
        dist[1] = 0;//初始化第一个点到自身的距离
        q.offer(new int[]{1, 0});
        while(q.size() != 0){
            int[] a = q.poll();
            int t = a[0], distance = a[1];
            if(f[t])continue;
            f[t] = true;
            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];
                   q.offer(new int[]{j, dist[j]});
               }
            }
        }
        if(dist[n] != 0x3f3f3f3f)return dist[n];
        return -1;
    }
    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)throws Exception{
        BufferedReader buf = new BufferedReader(new InputStreamReader(System.in));
        String[] params = buf.readLine().split(" ");
        n = Integer.valueOf(params[0]);
        m = Integer.valueOf(params[1]);
        Arrays.fill(h, -1);
        for(int i = 1; i <= m; ++i){
            String[] info = buf.readLine().split(" ");
            int a = Integer.valueOf(info[0]);
            int b = Integer.valueOf(info[1]);
            int c = Integer.valueOf(info[2]);
            add(a, b, c);
        }
        System.out.print(Dijkstra());

    }
}

作者:ARM
链接:https://www.acwing.com/solution/content/18110/
来源:AcWing

(三)Bellman-Ford算法(适合有边数限制的有负权边图问题)


        Bellman - ford 算法是求含负权图的单源最短路径的一种算法,效率较低,代码难度较小。其原理为连续进行松弛,在每次松弛时把每条边都更新一下,若在 n-1 次松弛后还能更新,则说明图中有负环,因此无法得出结果,否则就完成。
(通俗的来讲就是:假设 1 号点到 n 号点是可达的,每一个点同时向指向的方向出发,更新相邻的点的最短距离,通过循环 n-1 次操作,若图中不存在负环,则 1 号点一定会到达 n 号点,若图中存在负环,则在 n-1 次松弛后一定还会更新)

模版:for n次
                for 所有边 a,b,w (松弛操作)
                        dist[b] = min(dist[b],back[a] + w)

时间复杂度 O(nm)
其中n为点数,m为边数

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;
    }
}

作者:小呆呆
链接:https://www.acwing.com/solution/content/6320/
来源:AcWing

(四)SPFA算法(万金油)

算法分析
1、什么是spfa算法?
SPFA 算法是 Bellman-Ford算法 的队列优化算法的别称,通常用于求含负权边的单源最短路径,以及判负权环。SPFA一般情况复杂度是O(m)
 最坏情况下复杂度和朴素 Bellman-Ford 相同,为O(nm)

bellman-ford算法操作如下:
        for n次
                for 所有边 a,b,w (松弛操作)
                        dist[b] = min(dist[b],back[a] + w)

spfa算法对第二行中所有边进行松弛操作进行了优化,原因是在bellman—ford算法中,即使该点的最短距离尚未更新过,但还是需要用尚未更新过的值去更新其他点,由此可知,该操作是不必要的,我们只需要找到更新过的值去更新其他点即可。

2、spfa算法步骤
queue <– 1
while queue 不为空
 (1) t <– 队头
 queue.pop()
 (2)用 t 更新所有出边 t –> b,权值为w
 queue <– b (若该点被更新过,则拿该点更新其他点)

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

 (五)Floyd算法(多源)

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]);
}

总结: 

(6)最小生成树

       给定一个无向图,在图中选择若干条边把图的所有节点连起来。要求边长之和最小。在图论中,叫做求最小生成树。

(一)Prim算法

prim 算法采用的是一种贪心的策略。

每次将离连通部分的最近的点和点对应的边加入的连通部分,连通部分逐渐扩大,最后将整个图连通起来,并且边长之和最小。

伪代码:

int dist[n],state[n],pre[n];
dist[1] = 0;
for(i : 1 ~ n)
{
    t <- 没有连通起来,但是距离连通部分最近的点;
    state[t] = 1;
    更新 dist 和 pre;
}

详细见链接:https://www.acwing.com/solution/content/38312/

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;

public class Main {
static final int N=510;
static int[][] g=new int [N][N];//存图
static int[] st=new int[N];//存每个点的状态
static int[] pre=new int[N];//节点的前驱节点
static int[] dist=new int[N];
static int n,m;
    public static void prim() {
		int res=0;
		Arrays.fill(dist, 0x3f3f3f3f);
		Arrays.fill(st,0);
		dist[1]=0;
		for(int ia=0;ia<n;ia++) {
			int t=-1;
			for(int j=1;j<=n;j++) {
				if(st[j]==0&&(t==-1||dist[j]<dist[t]))
					t=j;
			}
			if(dist[t] == 0x3f3f3f3f) {
	           System.out.print("impossible");
	           return;
	        }
			st[t]=1;
            res += dist[t];
			//更新其他的点
			for(int i = 1; i <= n; i++)//更新生成树外的点到生成树的距离
        {
            if(dist[i] > g[t][i] && st[i]==0)//从 t 到节点 i 的距离小于原来距离,则更新。
            {
                dist[i] = g[t][i];//更新距离
                pre[i] = t;//从 t 到 i 的距离更短,i 的前驱变为 t.
            }
        }
		}
		System.out.print(res);
	}
   public static void main(String[] args) throws IOException {
	  BufferedReader br =new BufferedReader(new InputStreamReader(System.in)) ;
	  String[] s1=br.readLine().split(" ");
	  n=Integer.parseInt(s1[0]);
	  m=Integer.parseInt(s1[1]);
	  //记得初始化这个数组,不然未初始化的地方值为0,都要比其他地方短
	  for (int i = 0; i < N; i++) {
            Arrays.fill(g[i], 0x3f3f3f3f);
        }
        
	  for(int i=0;i<m;i++) {
		  int a,b,w;
		  String[] s2=br.readLine().split(" ");
		  a=Integer.parseInt(s2[0]);
		  b=Integer.parseInt(s2[1]);
		  w=Integer.parseInt(s2[2]);
		  g[a][b] = g[b][a] = Math.min(g[a][b], w);
	  }
	  
	 prim();
	  
}

}

(二)Kruskal算法

算法思路:

将所有边按照权值的大小进行升序排序,然后从小到大一一判断。

如果这个边与之前选择的所有边不会组成回路,就选择这条边分;反之,舍去。

直到具有 n 个顶点的连通网筛选出来 n-1 条边为止。

筛选出来的边和所有的顶点构成此连通网的最小生成树。

判断是否会产生回路的方法为:使用并查集。

在初始状态下给各个个顶点在不同的集合中。

遍历过程的每条边,判断这两个顶点的是否在一个集合中。

如果边上的这两个顶点在一个集合中,说明两个顶点已经连通,这条边不要。如果不在一个集合中,则要这条边。

详细见:https://www.acwing.com/solution/content/104383/

思路:1.所有的边按照权重从小到大进行排序

2.枚举每一条边a, b, 权重w
  if a,b 不连通
    将这条边加入到集合中

import java.util.Arrays;
import java.util.Scanner;

public class Main {
    static int N = 200010, INF = 0x3f3f3f3f;
    static int n, m;
    static int[] p = new int[N];
    static Edge[] edges = new Edge[N];

    private static class Edge implements Comparable<Edge>{
        private int a;
        private int b;
        private int w;

        Edge(int a, int b, int w){
            this.a = a;
            this.b = b;
            this.w = w;
        }

        @Override
        public int compareTo(Edge edge) {
            return Integer.compare(w, edge.w);
        }
    }

    private static int find(int x){
        if (x != p[x]) {
            p[x] = find(p[x]);
        }
        return p[x];
    }

    private static int kruskal() {
        int res = 0;
        int cnt = 0;

        Arrays.sort(edges, 0, m);

        for (int i = 0; i < m; i++) {
            Edge edge = edges[i];
            int a = edge.a;
            int b = edge.b;
            int w = edge.w;
            a = find(a);
            b = find(b);
            if (a != b) {
                p[a] = b;
                res += w;
                cnt ++;
            }
        }
        if (cnt < n - 1) return INF;
        return res;
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        m = sc.nextInt();

        for (int i = 0; i < m; i ++) {
            int a = sc.nextInt();
            int b = sc.nextInt();
            int w = sc.nextInt();
            edges[i] = new Edge(a, b, w);
        }

        for (int i = 1; i <= n; i ++) {
            p[i] = i;
        }

        int t = kruskal();
        if(t == INF) System.out.println("impossible");
        else System.out.println(t);

    }
}

作者:派大星的梦想
链接:https://www.acwing.com/solution/content/34242/
来源:AcWing

(7)染色法判断二分图

开始对任意一未染色的顶点染色。

判断其相邻的顶点中,若未染色则将其染上和相邻顶点不同的颜色。

若已经染色且颜色和相邻顶点的颜色相同则说明不是二分图,若颜色不同则继续判断。

bfs和dfs可解

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;

public class Main {
static final int N=100010;
static int[] e=new int[N*2];//存每个点的状态
static int[] ne=new int[N*2];//节点的前驱节点
static int[] h=new int[N];
static int[] color=new int[N];
static int n,m;
static int idx=0;
   public static void add(int a,int b) {
	   e[idx]=b;
	   ne[idx]=h[a];
	   h[a]=idx++;
   }
   public static boolean dfs(int u,int c) {//给点u染上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]!=3-c) {
			   return false;
		   }
	   }
	   return true;
   }
   public static void main(String[] args) throws IOException {
	  BufferedReader br =new BufferedReader(new InputStreamReader(System.in)) ;
	  String[] s1=br.readLine().split(" ");
	  n=Integer.parseInt(s1[0]);
	  m=Integer.parseInt(s1[1]);
	  Arrays.fill(h, -1);
	  for(int i=0;i<m;i++) {
		  int a,b;
		  String[] s2=br.readLine().split(" ");
		  a=Integer.parseInt(s2[0]);
		  b=Integer.parseInt(s2[1]);
          add(a,b);
          add(b,a);
	  }
      for(int i=1;i<=n;i++) {
    	  if(color[i]==0)//如果没染色
          {
              if(!dfs(i, 1))//染色该点,并递归处理和它相邻的点
              {
                  System.out.print("No");
                  return;
              }

          }
      }
      System.out.print("Yes");//全部染色完成,没有矛盾,输出YES
	  
}

}

(8)匈牙利算法:二分图的最大匹配

详解:AcWing 861. 二分图的最大匹配----图解--海绵宝宝来喽 - AcWing


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;

public class Main {
static final int N=100010;
static final int M=510;
static int[] e=new int[N];//存每个点的状态
static int[] ne=new int[N];//节点的前驱节点
static int[] h=new int[M];
static int[] st=new int[M];//标记是否找到匹配
static int[] match=new int[M];//match[x]:和 x 编号的男生的编号
static int n1,n2,m;
static int idx=0;
   public static void add(int a,int b) {
	   e[idx]=b;
	   ne[idx]=h[a];
	   h[a]=idx++;
   }
   static boolean find(int x) {
	   //尝试与各点匹配
	   for(int i=h[x];i!=-1;i=ne[i]) {
		   int j=e[i];
		   if(st[j]==0) {
			   st[j]=1;
			// 当前尝试点没有被匹配或者和当前尝试点匹配的那个点可以换另一个匹配
	            if(match[j] == 0 || find(match[j])){
	                // 和当前尝试点匹配在一起
	                match[j] = x;
	                return true;
	            }
		   }
	   }
	   return false;
   }
   public static void main(String[] args) throws IOException {
	  BufferedReader br =new BufferedReader(new InputStreamReader(System.in)) ;
	  String[] s1=br.readLine().split(" ");
	  n1=Integer.parseInt(s1[0]);
	  n2=Integer.parseInt(s1[1]);
	  m=Integer.parseInt(s1[2]);
	  Arrays.fill(h, -1);
	  for(int i=0;i<m;i++) {
		  int a,b;
		  String[] s2=br.readLine().split(" ");
		  a=Integer.parseInt(s2[0]);
		  b=Integer.parseInt(s2[1]);
          add(a,b);
	  }
	  int res=0;
      for(int i=1;i<=n1;i++) {
    	  Arrays.fill(st, 0);
    	  if(find(i))res++;
      }
	  System.out.print(res);
}

}

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值