最佳匹配问题

最佳匹配是我们经常会遇到的问题,我们通常使用匈牙利算法或KM算法解决这类问题。这类问题离不开权值的比较,也就是我们最爱举的例子——男女最佳匹配中的某个男孩更爱某个女孩的具体数值了。这里我找了个大神的讲解,非常通俗易懂,具体如下。
点击这里
我们可以这么理解,第一次,男生都向最喜欢的告白,女生当然只能接受数值最高那个啦,但这只是暂时的。第二次男生吸取教训,被拒绝的男生把拒接他的女生的名字从名单中去掉,向第二喜欢的告白,同第一次一样,女生继续拒绝。同样是暂时的。当所有人都有匹配对象时,匹配结束,这是即使最佳匹配。
(ps:估计也大三大四了吧。。。)
好,了解了以后,我们来做一道题。
例题
代码如下

import java.util.Scanner;
public class trash {
	static int max =1000;
	static int inf =0x3f3f3f3f;
	static int n;//垃圾桶数量 垃圾种类
	static int ans;
	static int en;
	static int st;
	static int cap[][]=new int[max][max];//是否有关系 关系路径
	static int	pre[]=new int[max];//确认关系 关系路径(最终)
	static int cost[][]=new int[max][max],//除了第j种垃圾 第i个垃圾箱有多少垃圾(最终)
					dis[]=new int[max];
	static int que[]=new int[max];//连线
	static boolean vis[]=new boolean[max];
	static int r[][]=new int[max][max],
			s[]=new int[max];
	static boolean spfa(){
		int i, head = 0, tail = 1;
	    for(i = 1; i <= n; i ++){//n=2n+2
	        dis[i] = inf;
	        vis[i] = false;
	    }
	    dis[st] = 0;//st=2n+1
	    que[0] = st;
	    vis[st] = true;
	    while(tail != head){      //  循环队列。
	        int u = que[head];//既2n+1
	        for(i = 1; i <= n; i ++)
	            if(cap[u][i]==1 && dis[i] > dis[u] + cost[u][i]){    //  存在路径
	                dis[i] = dis[u] + cost[u][i];//除了第i种垃圾 第u个垃圾箱有多少垃圾
	               // System.out.println(dis[i]);
	                pre[i] = u;
	                if(!vis[i]){
	                    vis[i] = true;//已占用 更合适
	                    que[tail] = i;
	                    //System.out.println(dis[i]+" "+tail+" "+i);
	                    tail ++;
	                    if(tail == max) tail = 0;
	                }
	            }
	        vis[u] = false;
	        head ++;
	        if(head == max) head = 0;
	    }
	    if(dis[en] == inf) return false;
	    return true;	
	}
	static void end(){
	    int i, sum = inf;
	    for(i = en; i != st; i = pre[i])
	    	sum =sum<cap[pre[i]][i]?sum:cap[pre[i]][i];//取小值
	    for(i = en; i != st; i = pre[i]){//从第1种垃圾开始累加
	        cap[pre[i]][i] = cap[pre[i]][i]-sum;//已使用
	        cap[i][pre[i]] = cap[i][pre[i]] +sum;//无效了
	        ans += cost[pre[i]][i] * sum;   //  cost[][]记录的为单位流量费用,必须得乘以流量。
	   // System.out.println(ans);
	    }
	   
	}
public static void main(String[] args){
	  Scanner ss = new Scanner(System.in);
      n = ss.nextInt();//垃圾桶数量
      int i,j;
       for(i=1;i<=n;i++){
     	 for(j=1;j<=n;j++){
     		 r[i][j+n]=ss.nextInt();
 			s[i]+=r[i][j+n];//第i个垃圾桶总垃圾
     	}        	 
       }
       for(i=1;i<=n;i++)
   		 for(j=1;j<=n;j++)
   		{
   			
   			cost[i][j+n]=s[i]-r[i][j+n];//除了第j种垃圾 第i个垃圾箱有多少垃圾
   			cap[i][j+n]=1;
   			cap[j+n][i]=0;
   			cost[j+n][i]=-cost[i][j+n];
   			
   		}       
   		st=2*n+1;
   		en=2*n+2;
   		for(i=1;i<=n;i++)
   		{
   			cap[st][i]=1;//确认关系
   			cap[i][st]=0;//清除关系
   			cost[st][i]=cost[i][st]=0;
   			cap[i+n][en]=1;
   			cap[en][i+n]=0;
   			cost[en][i+n]=cost[i+n][en]=0;  				
   		}
   	 ans = 0;  
   	n=2*n+2;
       while(spfa()==true)
    	   {
    	   end();
    	   }  
      
      System.out.println(ans);
       return;      
}	 
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值