zoukankan      html  css  js  c++  java
  • 「专题训练」Machine Schedule(HDU-1150)

    题意

    在一个工厂,有两台机器(A, B)生产产品。(A)机器有(n)种工作模式(模式(0),模式(1)……模式(n-1))。(B)机器有(m)种工作模式(模式(0),模式(1)……模式(m-1))。现要加工k个产品,每个产品可以由两台机器特定的模式生产,如产品0,可以由A机器在3号模式或B机器4号模式生产。两台机器初始模式都在模式0,但是,这两台机器不是很先进,如果需要切换模式,只能由人手工切换模式,手工切换可以切换到任意模式。求加工完k个产品需要切换模式的最少次数。(生产产品的顺序可以任意)

    分析

    如何建模?把A、B机器的模式抽象成点,然后把每个产品抽象成边,如果可以在((A_i,B_j))下生产,建一条边(对于一个顶点为0的不建边)。然后求最少的点(模式)以覆盖所有的边(产品)。
    于是成功的抽象成二分图的经典问题。
    思考一下,为什么二分图的最小顶点覆盖能够通过二分图匹配来做。

    1. 首先,最小顶点覆盖数一定大于等于最大匹配数。不妨假设最大匹配为(n),那么一定有(n)条互不相邻的边,对这些边的覆盖至少要(n)个点。
    2. 上面考虑了匹配内的边/点。对于匹配范围外的节点,有两种情况:一种就是有边和匹配范围内元素相连但是没有匹配到,一种就是没边。对于第二种情况当作无事发生过,而对于第一种情况,有边的话这个边就和匹配范围的顶点打交道了,那这个顶点覆盖代表元素就又能够连接上匹配边,又能同非匹配边相连,这样,就不需要增加额外的顶点了。因此,我们有结论:匹配范围外的所有节点都不可能影响到最小顶点覆盖数,所以两者完全相等。

    代码

    这份代码会更为具体的解释相关细节。有错误欢迎指正,谢谢茄子。

    #include <iostream>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #define MP make_pair
    #define PB push_back
    #define fi first
    #define se second
    #define ZERO(x) memset((x), 0, sizeof(x))
    #define ALL(x) (x).begin(),(x).end()
    #define rep(i, a, b) for (int i = (a); i <= (b); ++i)
    #define per(i, a, b) for (int i = (a); i >= (b); --i)
    #define QUICKIO                  
        ios::sync_with_stdio(false); 
        cin.tie(0);                  
        cout.tie(0);
    #define MS(x,y) memset(x,y,sizeof(x))
    using namespace std;
    
    bool mat[105][105],vis[105];
    int n,m;
    int link[105];
    
    bool dfs(int x) //通过dfs拓展增广路,总是从左侧(u->v)开始。如果是个无向图的话,
    {                     //就不仅仅从左侧开始了,那种情况下的匹配要除以2。
    	rep(i,1,m)
    	{
    		if(mat[x][i] && !vis[i]) //有边,未拓展过
    		{
    			vis[i]=true;
    			if(link[i]==-1 || dfs(link[i])) //这个link[i]数组干什么吃的呢?用来标记i点走的上一个点的(邻接矩阵)。
    			{                                       //什么情况下会返回true?1. 从x点出发到的这个i点没被访问过(走了一条未匹配边)
    				link[i]=x;                   //2.i被访问过,有一条边(link[i]=>i),但是存在一条未访问边能够从link[i]走到其它地方
    				return true;              //这样的情况下就是匹配边->未匹配边,发现增广路。这两种情况下找匹配。
    			}                                      //而且对增广路的改进能够保证x点出发有边可以走,所以返回true的时候能够让答案++。
    		}
    	}
    	return false; // 这个点没法再构成增广路了,也就没法再改进匹配了。
    }
    
    int hungary()
    {
    	int ans=0;
    	rep(i,1,n)
    	{
    		ZERO(vis);
    		if(dfs(i)) ans++; //一个点只能够构成一个匹配
    	}
    	return ans;
    }
    
    int main()
    {
    	int k;
    	while(scanf("%d",&n)==1)
    	{
    		if(!n) break;
    		scanf("%d%d",&m,&k);
    		ZERO(mat);
    		ZERO(vis);
    		MS(link,-1);
    		rep(i,1,k)
    		{
    			int a,b,ti;
    			scanf("%d%d%d",&ti, &a, &b);
    			if(a && b) mat[a][b]=1;
    		}
    		printf("%d
    ", hungary());
    	}
    	return 0;
    }
    
    
    如非注明,原创内容遵循GFDLv1.3发布;其中的代码遵循GPLv3发布。
  • 相关阅读:
    JAVA之数组
    Linux之判断字符串是否为空
    Python之操作HBASE数据库
    【转】Linux之crontab定时任务命令
    Python之shutil模块(复制移动文件)
    JAVA之列表集合ArrayList
    Python之多线程多进程
    前端之Javascript
    前端之DOM操作
    【转】写一个简单的爬虫来批量爬取新浪网的新闻
  • 原文地址:https://www.cnblogs.com/samhx/p/HDU-1150.html
Copyright © 2011-2022 走看看