图论:最短路算法、最小生成树以及二分图

一、最短路算法

稠密图:邻接矩阵存储
稀疏图:邻接表存储(链式前向星)
规定n为点数 m为边数
在这里插入图片描述

1.朴素版的Dijkstra算法(当m等于或者接近n^2时使用)

//狄杰斯特拉算法
public class Main {
	static final int N=510;
	static int n,m;
	static int[][] g=new int[N][N];
	static int[] dist=new int[N];
	static boolean[] st=new boolean[N];
	public static int Dijkstra() {
		Arrays.fill(dist,0x3f3f3f3f);
		dist[1]=0;
		for(int i=0;i<n;i++) {
			int t=-1;
			for(int j=1;j<=n;j++) 
				if(!st[j]&&(t==-1||dist[j]<dist[t]))
					t=j;
			st[t]=true;
			for(int j=1;j<=n;j++) 
				dist[j]=Math.min(dist[j],dist[t]+g[t][j]);
		}
		return dist[n]==0x3f3f3f3f?-1:dist[n];
	}
}

2.堆优化版的Dijkstra算法(当m等于或接近n时使用)

//狄杰斯特拉算法
public class Main {
	static int n,m;
	static final int N=150010;
	static int[] dist=new int[N];
	static boolean[] st=new boolean[N];
	static int[] h=new int[N],e=new int[N],ne=new int[N],w=new int[N];
	//实现小根堆
	static PriorityQueue<int[]> min=new PriorityQueue<int[]>(new Comparator<int[]>() {
		public int compare(int[] o1,int[] o2) {
			return o1[1]-o2[1];
		}
	});
	public static int Dijkstra() {
		Arrays.fill(dist,0x3f3f3f3f);
		dist[1]=0;
		min.offer(new int[] {1,dist[1]});
		while(!min.isEmpty()) {
			//出队
			int[] cur=min.poll();
			int ver=cur[0];
			int distance=cur[1];
			if(st[ver]) continue;
			st[ver]=true;
			for(int i=h[ver];i!=-1;i=ne[i]) {
				int j=e[i];
				if(dist[j]>distance+w[i]) {
					dist[j]=distance+w[i];
					min.offer(new int[] {j,dist[j]});
				}
			}
		}
		return dist[n]==0x3f3f3f3f?-1:dist[n];
	}
}

3.Bellman-ford算法(有边数限制时使用)

//贝尔曼-福特算法
public class Main {
	static final int N=510,M=10010;
	static int n,m,k;
	static pair11[] edge=new pair11[M];
	static int[] backup=new int[N];  //备份数组
	static int[] dist=new int[N];   
	public static int bellman_ford() {
		Arrays.fill(dist,0x3f3f3f3f);
		dist[1]=0;
		//循环k次 即最多经过k条边的最短路径
		for(int i=1;i<=k;i++) {
			System.arraycopy(dist,1,backup,1,dist.length-1);
			for(int j=1;j<=m;j++) {
				int a=edge[j].x;
				int b=edge[j].y;
				int c=edge[j].z;
				dist[b]=Math.min(dist[b],backup[a]+c);
			}
		}
		return dist[n]>0x3f3f3f3f/2?0x3f3f3f3f:dist[n];
	}
}
//存边
class pair11{
	int x,y,z;
	public pair11() {
	}
	public pair11(int a,int b,int c) {
		x=a;y=b;z=c;
	}
}

4.spfa算法(存在负权边且无边数限制时使用)

//spfa算法
//1.spfa算法求最短路
public class Main {
	static int n,m,idx=1;
	static final int N=100010;
	static int[] h=new int[N],e=new int[N],ne=new int[N],w=new int[N];
	static int[] dist=new int[N];
	static boolean[] st=new boolean[N];
	static Queue<Integer> q=new LinkedList<Integer>();
	public static int spfa() {
		Arrays.fill(dist,0x3f3f3f3f);
		dist[1]=0;
		q.offer(1);
		st[1]=true;
		while(!q.isEmpty()) {
			int t=q.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]) {
						st[j]=true;
						q.offer(j);
					}
				}
			}
		}
		return dist[n];
	}
}

//2.spfa算法来求是否存在负环
public class Main {
	static int n,m;
	static int N=2010,M=10010,idx=1;
	static int[] dist=new int[N];
	static int[] h=new int[N],e=new int[M],ne=new int[M],w=new int[M];
	static boolean[] st=new boolean[N];
	static int[] cnt=new int[N];
	static Queue<Integer> q=new LinkedList<Integer>();
	public static boolean spfa() {
		for(int i=1;i<=n;i++) {
			q.offer(i);
			st[i]=true;
		}
		while(!q.isEmpty()) {
			//出队
			int t=q.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]) {
						st[j]=true;
						q.offer(j);
					}
				}
			}
		}
		return false;
	}
}

5.floyd算法(多源汇最短路问题时使用)

//弗洛伊德算法
//运用动态规划思想
public class Main{
    static int N=210;
    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]);
    }
}

二、最小生成树

稠密图一般用朴素版的prim算法
稀疏图一般用克鲁斯卡尔算法和堆优化版的prim算法,堆优化版的prim算法一般不使用(很少用)。
在这里插入图片描述

1.朴素版Prim算法(稠密图时使用)

import java.util.Scanner;
import java.util.Arrays;
public class Main {
    static int N=510,M=(int)1e5+10;
    static int INF=0x3f3f3f3f;
    static int[] dist=new int[N];
    static int[][] g=new int[N][N];
    static boolean[] st=new boolean[N];
    static int n,m;
    public static int prim() {
        Arrays.fill(dist,INF);
        int res=0;
        for(int i=1;i<=n;i++) {
            int t=-1;
            for(int j=1;j<=n;j++)
                if(!st[j]&&(t==-1||dist[j]<dist[t]))
                    t=j;
            if(i!=1&&dist[t]==INF) return INF;
            if(i!=1) res+=dist[t];
            for(int j=1;j<=n;j++)
                dist[j]=Math.min(dist[j],g[t][j]);
            st[t]=true;
        }
        return res;
    }
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        int u,v,w;
        n=sc.nextInt();
        m=sc.nextInt();
        for(int i=1;i<=n;i++) {
            Arrays.fill(g[i],INF);
        }
        while(m--!=0) {
            u=sc.nextInt();
            v=sc.nextInt();
            w=sc.nextInt();
            g[u][v]=g[v][u]=Math.min(g[u][v],w);
        }
        int t=prim();
        System.out.println(t==INF?"impossible":t);
    }
}

2.Kruskal算法(稀疏图使用)

import java.util.*;
public class Main{
    static int N=100010,M=2*100010;
    static int n,m;
    static int[] f=new int[N];
    static pair10[] edge=new pair10[M];
    public static int find(int x) {
    	if(f[x]!=x)
    		f[x]=find(f[x]);
    	return f[x];
    }
    public static int kruskal() {
    	Arrays.sort(edge,1,m+1,new Comparator<pair10>(){
        	public int compare(pair10 o1,pair10 o2){
        		return o1.c-o2.c;
        	}
        });
    	int res=0,cnt=0;
    	for(int i=1;i<=m;i++) {
    		int a=edge[i].a;
    		int b=edge[i].b;
    		int c=edge[i].c;
    		a=find(a);
    		b=find(b);
    		if(a!=b) {
    			f[a]=b;
    			res+=c;
    			cnt++;
    		}
    	}
    	return cnt<n-1?0x3f3f3f3f:res;
    }
}
class pair10{
    int a,b,c;
    public pair10(int x,int y,int z){
        a=x;
        b=y;
        c=z;
    }
}

三、二分图

在这里插入图片描述

1.二分图定义

   二分图:当且仅当图中不含有奇数环(环的边数为奇数)
   二分图也称为二部图,是图论中的一种特殊模型,也是一种特殊的网路流,最大的特点在于,可以将图中的顶点划分为两个集合,且同一集合内的点没有直接关联。通俗点说,实际上就是二分图可以将顶点划分到两个集合当中,使得同一集合中的点之间没有边,边只存在于两个集合的点之间。

2.二分图判定—染色法

import java.util.Arrays;
import java.util.Scanner;
public class Main {
	static int N=(int)1e5+10,M=(int)2e5+10;
	static int n,m,idx=1;
	static int[] h=new int[N],color=new int[N];
	static int[] e=new int[M],ne=new int[M];
	public static void addedge(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;
		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) {
		Scanner sc=new Scanner(System.in);
		Arrays.fill(h,-1);
		n=sc.nextInt();
		m=sc.nextInt();
		for(int i=1;i<=m;i++) {
			int a=sc.nextInt();
			int b=sc.nextInt();
			addedge(a,b);
			addedge(b,a);
		}
		boolean flag=true;
		for(int i=1;i<=n;i++) {
			if(color[i]==0) {
				if(!dfs(i,1)) {
					flag=false;
					break;
				}
			}
		}
		System.out.println(flag==true?"Yes":"No");
	}
}

3.二分图最大匹配—匈牙利算法

import java.util.Arrays;
import java.util.Scanner;
public class acw861 {
	static int n1,n2,m,idx=1;
	static int N=510,M=(int)1e5+10;
	static int[] h=new int[N],match=new int[N];
	static int[] e=new int[M],ne=new int[M];
	static boolean[] st=new boolean[N];
	public static void addedge(int a,int b) {
		e[idx]=b;
		ne[idx]=h[a];
		h[a]=idx++;
	}
	public 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) {
		Scanner sc=new Scanner(System.in);
		n1=sc.nextInt();
		n2=sc.nextInt();
		m=sc.nextInt();
		Arrays.fill(h,-1);
		for(int i=1;i<=m;i++) {
			int a=sc.nextInt();
			int b=sc.nextInt();
			addedge(a,b);
		}
		int res=0;
		for(int i=1;i<=n1;i++) {
			Arrays.fill(st,false);
			if(find(i))
				res++;
		}
		System.out.println(res);
	}
}
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值