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\n", MaxMatch());
    }
}
posted @ 2017-12-28 20:57  AlvinZH  阅读(561)  评论(0编辑  收藏  举报