算法学习记录 8.17 最短路(Floyd算法) 最小生成树(Prim算法 Kruskal算法)染色法判定二分图 匈牙利算法

本文介绍了四种图论中的经典算法:Floyd算法用于求解所有两点之间的最短路径,Prim和Kruskal算法用于寻找最小生成树,以及二分图的染色法判定和匈牙利算法解决配对问题。详细展示了算法实现和应用场景。
摘要由CSDN通过智能技术生成

1.Floyd算法

基于动态规划

思想见最短路径模板+解析——(FLoyd算法)_floyd其时间复杂度的解释_coderyzh的博客-CSDN博客

 题解

import java.util.*;

public class Main{
    static int max=0x3f3f3f3f,N=210;
    static int n,m,k;
    static int[][] d=new int[N][N];
    public static 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]=Math.min(d[i][j],d[i][k]+d[k][j]);
                }
            }
        }
    }
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        n=sc.nextInt();
        m=sc.nextInt();
        k=sc.nextInt();
        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]=max;
            }
        }
        while(m-->0){
            int x=sc.nextInt();
            int y=sc.nextInt();
            int z=sc.nextInt();
            d[x][y]=Math.min(d[x][y],z);
        }
        floyd();
        while(k-->0){
            int a=sc.nextInt();
            int b=sc.nextInt();
            if(d[a][b]>max/2) System.out.println("impossible");
            else System.out.println(d[a][b]);
        }
    }
}

 

 2.Prim算法

朴素版Prim

算法思想;

1.初始化dist数组为最大值

2.遍历dist数组 for(int i=0;i<n;i++)

3.找到集合外距离最近的点并赋给t

4.用t来更新其他点到集合的距离

5.st[t]=true;

 题解

import java.util.*;

public class Main{
    static int n,m;
    static int max=0x3f3f3f3f;
    static int N=510;
    static int[][] g=new int[N][N];
    static int[] dist=new int[N];
    static boolean[] st=new boolean[N];
    public static int prim(){
        Arrays.fill(dist,max);//dist数组初始化
        dist[1]=0;
        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(dist[t]==max) return max;//表示点没有连通 说明不是连通图 不存在最小生成树
            res+=dist[t];//连通图 加上集合外的点中离集合最近点到集合的最短距离
            st[t]=true;//将该点加入集合
            //用这个点去更新其他的点到集合的距离
            for(int j=1;j<=n;j++){ 
                if(!st[j])
                dist[j]=Math.min(dist[j],g[t][j]);
            }
            
        }
        return res;
    }
    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],max);
        while(m-->0){
            int a=sc.nextInt();
            int b=sc.nextInt();
            int c=sc.nextInt();
            g[a][b]=g[b][a]=Math.min(g[a][b],c);
            
        }
        int t=prim();
        if(t==max) System.out.println("impossible");
        else System.out.println(t);
    }
}

 3.Kruskal算法

算法思想:

1.将所有边按权重从小到大排序

2.枚举每条边 a,b,权重c

       如果a,b不连通,将其加入集合中

题目描述同上

题解

import java.util.*;

public class Main{
    static int n,m;
    static int N=200010;
    static int[] p=new int[N];
    static Pair[] list=new Pair[N];
    //找祖宗结点
    public static int find(int x){
        if(p[x]!=x) p[x]=find(p[x]);
        return p[x];
    }
    public static void kruskal(){
        //数组排序
        Arrays.sort(list,0,m);
        int res=0;//记录权重和
        int cnt=0;//记录集合当中点的数量
        for(int i=0;i<m;i++){
            int a=list[i].a;
            int b=list[i].b;
            int w=list[i].w;
            a=find(a);
            b=find(b);
            if(a!=b){//如果a和b不是一个祖宗结点 说明a和b不在一个连通块中 加入其中
                p[a]=b;
                res+=w;
                cnt++;
            } 
        }
        //cnt<n-1说明存在点是不连通的 无最小生成树
        if(cnt<n-1) System.out.println("impossible");
        else System.out.println(res);
    }
    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++) p[i]=i;
        for(int i=0;i<m;i++){
            int a=sc.nextInt();
            int b=sc.nextInt();
            int w=sc.nextInt();
            list[i]=new Pair(a,b,w);
        }
        kruskal();
    }
}
class Pair implements Comparable<Pair>{
    int a,b,w;
    public Pair(int a,int b,int w){
        this.a=a;
        this.b=b;
        this.w=w;
    }
    public int compareTo(Pair o){
        return Integer.compare(w,o.w);
    }
}

kruskal算法的时间复杂度主要是对权重排序时带来的mlogm

4.染色法判定二分图

二分图中不存在奇数结点的环

算法思想:

for(int i=1;i<=n;i++)

     if   i 未被染色

         dfs(i,颜色);

题解

import java.util.*;

public class Main{
    static int n,m,idx;
    static int N=100010,M=200010;
    static int[] e=new int[M];
    static int[] ne=new int[M];
    static int[] h=new int[N];
    static int[] color=new int[N];//存储每个结点对应的颜色
    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){
        color[u]=c;//给第u个结点染颜色c
        //遍历邻接表
        for(int i=h[u];i!=-1;i=ne[i]){
            int j=e[i];
            if(color[j]==0){//如果结点j未被染色
                //递归深搜j结点 染色3-c(颜色1和颜色2)
                if(!dfs(j,3-c)) return false;
            }//如果与u相邻结点j的颜色也是c则说明不是二分图
            else if(color[j]==c) return false;
        }
        return true;
    }
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        n=sc.nextInt();
        m=sc.nextInt();
        Arrays.fill(h,-1);
        while(m-->0){
            int a=sc.nextInt();
            int b=sc.nextInt();
            add(a,b);
            add(b,a);
        }
        boolean flag=true;
        //遍历一遍所有结点
        for(int i=1;i<=n;i++){
            //未被染色
            if(color[i]==0){
                //如果从第i个结点深度优先搜索染色不符合要求
                if(!dfs(i,1)){
                    flag=false;
                    break;
                }
            }
        }
        if(flag) System.out.println("Yes");
        else System.out.println("No");
    }
}

 5.匈牙利算法

 题意描述见匈牙利算法(简单易懂)_一只大秀逗的博客-CSDN博客

题解

import java.util.*;

public class Main{
    static int N=510,M=100010;
    static int n1,n2,m,idx;
    static int[] e=new int[M];
    static int[] ne=new int[M];
    static int[] h=new int[N];
    static int[] match=new int[N];//匹配数组 用于记录每位男生匹配的女生下标
    static boolean[] st=new boolean[N];

    public static void add(int a,int b){
        e[idx]=b;
        ne[idx]=h[a];
        h[a]=idx++;
    }
    public static boolean find(int x){
        //遍历所有与男生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;
                return true;
                }
            }
        }
        //否则匹配失败
        return false;
    }
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        int n1=sc.nextInt();
        int n2=sc.nextInt();
        int m=sc.nextInt();
        Arrays.fill(h,-1);
        while(m-->0){
            int a=sc.nextInt();
            int 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);
    }
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值