• 二分图匹配 匈牙利算法 最大匹配


    题目
    问题 E: Plug It In!
    题目描述
    Adam just moved into his new apartment and simply placed everything into it at random. This means in particular that he did not put any effort into placing his electronics in a way that each one can have its own electric socket.
    Since the cables of his devices have limited reach, not every device can be plugged into every socket without moving it first. As he wants to use as many electronic devices as possible right away without moving stuff around, he now tries to figure out which device to plug into which socket. Luckily the previous owner left behind a plugbar which turns one electric socket into 3.
    Can you help Adam figure out how many devices he can power in total?

    输入
    The input consists of:
    • one line containing three integers m, n and k, where
    – m (1 ≤ m ≤ 1 500) is the number of sockets;
    – n (1 ≤ n ≤ 1 500) is the number of electronic devices;
    – k (0 ≤ k ≤ 75 000) is the number of possible connections from devices to sockets.
    • k lines each containing two integers xi and yi indicating that socket xi can be used to power device yi .
    Sockets as well as electronic devices are numbered starting from 1.
    The plugbar has no cable, i.e. if it is plugged into a socket it simply triples it.

    输出
    Output one line containing the total number of electrical devices Adam can power.

    样例输入
    复制样例数据
    3 6 8
    1 1
    1 2
    1 3
    2 3
    2 4
    3 4
    3 5
    3 6
    样例输出
    5

    题目就是一个二分图,每个只能找一个进行匹配,但是题目这个地方改动了:就是有一个可以匹配三个,所以这个时候的做法就是先进行最初的匹配,也就是一对一的匹配,然后暴力枚举每个左边的点,也就是插头的点 , 然后新建两个点,表示枚举的这个点的两个“分身” , 具有该点的所有关系,然后在对这两个点进行一对一的匹配,也就相当于对枚举的这个点进行了三个匹配,当然了,程序中要及时进行更新,,具体见代码

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    int n , m , k ;
    const int N = 1e4 + 10 ;
    int vis[N] , ma[2000] ,remain[N] , map[2000][2000];
    //这个是基础的匈牙利极大匹配
    bool match(int x)
    {
    	for(int i = 1;i <= m;i ++)
    	 if(!vis[i] && map[x][i])
    	  {
    	  	vis[i] = 1 ;
    	  	if(ma[i] == 0 || match(ma[i]))
    	  	 {
    	  	 	ma[i] = x ;
    	  	 	return true ;
    		   }
    	  }
    	  return false ;
    }
    int main()
    {
    	
    	cin >> n >> m >> k ; // 这个地方我和题目设置的变量有点不一样
    	for(int i = 1;i <= k;i ++)
    	{
    		int a , b ;
    		cin >> a >> b ;
    		map[a][b] =  1 ;  
    		// 这个地方用了矩阵表示关系,本来想用链式前向星进行表示的,但是发现不太方便
    	}
    	int ans = 0 ;
    	for(int i = 1;i <= n;i ++)
    	 {
    	 	memset(vis , false , sizeof vis);
    	 	if(match(i)) ans ++ ;
    	 }
    	 //上面是先进行一次匹配,然后下面是进行暴力枚举每个点,对每个点新建两个分身
    	 for(int i = 1;i <= m;i ++)
    	  remain[i] = ma[i] ;
    	  //这个地方就是保持记录一下最初的二分图匹配关系,因为下面枚举每个点都是在原二分图的基础上 , 因为只增加一个结点上面的两个分身
    	 int maxn = 0 ;
    	 for(int i = 1;i <= n;i ++)
    	 {
    	 	//此时增加两个分身,具有原点的一切关系
    	 	for(int j = 1;j <= m;j ++) map[n + 1][j] = map[n + 2][j] = map[i][j] ;
    	 	//此时就相当于初始化一下,不过初始化成第一次匹配图
    	 	for(int j = 1;j <= m;j ++) ma[j] = remain[j] ;
            int now = 0 ;
            // 下面就是进行这两个分身进行一对一的匹配
            memset(vis , 0 , sizeof vis) ;
    		for(int j = n + 1;j <= n + 2 ;j ++)
             if(match(j))
              now ++ ;
    	 	maxn = max(maxn , now) ;
    	 }
    	 cout << ans + maxn << endl ;
    	return 0 ;
    } 
    
    每次做题提醒自己:题目到底有没有读懂,有没有分析彻底、算法够不够贪心、暴力够不够优雅。
  • 相关阅读:
    Oracle 常用的V$ 视图脚本
    Oracle11g新特性
    深入浅出Oracle:Redo的作用
    oracle表空间操作详解
    Oracle Directory
    linux下手动删除数据库实例
    Oracle OS认证以及口令文件
    监控Oracle数据库的常用shell脚本
    Oracle 帐号基本管理
    Oracle小知识
  • 原文地址:https://www.cnblogs.com/spnooyseed/p/12870922.html
Copyright © 2020-2023  润新知