zoukankan      html  css  js  c++  java
  • 2016级算法期末模拟练习赛-E.AlvinZH的青春记忆III

    1083 AlvinZH的青春记忆III

    思路

    难题,二分图。

    说这是一个考察二分图的题目,你可以会说“不可能”,这哪里像一个二分图了!这真的是一个二分图,考察的是最小顶点覆盖。

    你不知道最小顶点覆盖没有关系,别百度,先慢慢分析,别先入为主。

    简化题意。A、B两列数字,若B[i]%A[j]==0,则二者需要去掉一个。问一共最少需要去掉多少人!

    错误方法:二重循环记录可以整除的双方编号B[i]和A[j],记M1[A[j]]=M2[B[i]]=true,之后求M1、M2中为true的数量,取较小值输出。

    为什么这是错的?我反手给出一个反例:2,4,6与2,10,12,计算出M1、M2中为true的数量都是3,但是答案却为2,因为只要去掉A中的2和B中的12即可。

    错误的原因在于没有考虑可以两个数组中同时删去一些数据。那么对于两边都有可能删除数据的情况,该怎么处理呢?答案是不考虑。当然,我指的不是不考虑这种情况,而是不考虑点,转而考虑边,如果有B[i]%A[j]==0,则将来两个数之间连上一条边,表示需要删掉其中之一,我们的目标是删去其中一些点,使得两边不存在连线。

    不存在连线是什么意思?没错,就是不存在增广路径!

    增广路径的定义如下:若P是图G中一条联通两个未匹配顶点的路径,且属于M的边和不属于M的边在P上交替出现,则称P为相对于M的一条增广路径。

    那么又如何处理删去点呢?总不能尝试删去一个点,DFS删点过程中再DFS找增广路径吧,想想都可怕。这里借用二分图匹配来完成模拟删点。先来个定义转化,二分图的一个匹配(包含若干条边的集合,且其中任意两条边没有公共端点):只要删掉这个匹配中每条边的其中一个点,就可以把这些边抹去。如果还有剩下的边,那么代表原图一定存在增广路径,如果求得了二分图最大匹配,代表不存在增广路径,假设最大匹配数为x,那么一定存在一种方法,删去最大匹配中的x个点,使二分图的这个最大匹配完全“崩溃”,即匹配边都不存在了。注意,删点的方法肯定不能任意地选匹配边两点中的一点,由于刚刚已经不存在增广路径,也就是说未匹配边中的点肯定有一个最大匹配顶点,删点的过程中不仅可以删去匹配边,也可以保证把未匹配边一同删去。

    不知道你听明白了没有?说到底,这是一个二分图匹配的最小顶点覆盖。我竟然从实际的角度说明最小顶点覆盖=二分图最大匹配数,需要说明:理论上也已经证明。

    最小顶点覆盖:最少的顶点数使得二分图G中的每条边都至少与其中一个点相关联。二分图的最小顶点覆盖数=二分图的最大匹配数。

    很明显,我们找的就是这个最小顶点数,由于每条边都与其中一个点关联,如果把这个最小顶点数所对应顶点全部删去,那么所有的边也没了。

    接下来就是求二分图最大匹配了,不说了,参考AlvinZH的学霸养成记IV

    分析

    最小点覆盖练习题

    二营长,你他娘的意大利炮呢

    关于最小顶点覆盖=二分图的最大匹配参考

    二分图详解

    参考代码

    //
    // Created by AlvinZH on 2017/12/5.
    // Copyright (c) AlvinZH. All rights reserved.
    //
    
    #include <cstdio>
    #include <cstring>
    #include <vector>
    #define MaxSize 1005
    #define INF 0x3f3f3f3f
    using namespace std;
    
    int n, m, ans;
    vector<int> G[MaxSize];//记录匹配的双方弟子
    int match[MaxSize];//记录匹配点
    int visit[MaxSize];//记录是否访问
    
    int A[MaxSize], B[MaxSize];
    
    bool dfs(int x)//寻找增广路径
    {
        for (int i = 0; i < G[x].size(); ++i)
        {
            int to = G[x][i];
            if(!visit[to])
            {
                visit[to] = 1;
                if(!match[to] || dfs(match[to]))
                {
                    match[to] = x;
                    return true;
                }
            }
        }
        return false;
    }
    
    int MaxMatch()
    {
        ans = 0;
        memset(match, 0, sizeof(match));
        for (int i = 1; i <= m; ++i)
        {
            memset(visit, 0, sizeof(visit));//清空访问
            if(dfs(i)) ans++;//从节点i尝试扩展
        }
        return ans;
    }
    
    int main()
    {
        while(~scanf("%d %d", &n, &m))
        {
            for (int i = 1; i < MaxSize; ++i)
                G[i].clear();
            for (int i = 1; i <= n; ++i)
                scanf("%d", &A[i]);
            for (int i = 1; i <= m; ++i)
            {
                scanf("%d", &B[i]);
                for(int j = 1; j <= n; ++j)
                {
                    if(B[i] % A[j] == 0)
                        G[i].push_back(j);
                }
            }
    
            printf("%d
    ", MaxMatch());
        }
    }
    
  • 相关阅读:
    Oracle 修改带数据的字段类型
    Oracle的主键约束、唯一约束与外键约束
    Oracle 唯一 索引 约束 创建 删除
    Oracle 在Drop表时的Cascade Constraints
    iTunes备份注意
    谈判的四种风格
    求平均速度
    网站推荐的代码自动生成软件实际使用感触
    DOTA游戏相关的文章
    魔兽争霸3不能弹出输入法原因
  • 原文地址:https://www.cnblogs.com/AlvinZH/p/8137732.html
Copyright © 2011-2022 走看看