poj3020

本文深入探讨离散数学的概念及其在算法领域的应用,通过解析支配集、点独立集、点覆盖数等核心概念,展示如何将其应用于解决实际问题,如雷达覆盖优化,通过匈牙利算法求解二分图最大匹配,最终计算最小路径覆盖。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先明确几个概念

设G为无向图,有:

(1)点支配集

设G=<V,E>,V*属于V.
(1) V*为支配集——任意vi属于(V-V*),存在vj属于V*,使得(vi,vj)属于E 
(2) V*为极小支配集——V*的真子集不是支配集
(3) 最小支配集——元素最少的支配集
(4) 支配数γ0(G)——最小支配集中的元素个数

(2)点独立集

设G=<V,E>,V*属于V.
(1) 点独立集V*——V*中顶点彼此不相邻
(2) V*为极大点独立集——V*中再加入任何顶点就不是点独立集
(3) 最大点独立集——元素最多的点独立集
(4) 点独立数——最大点独立集中的元素个数,记为β0 

(3)点覆盖数

设G=<V,E>, V*属于V.
(1) V*是点覆盖集——任意e属于E,存在v属于V*,使e与v关联
(2) V*是极小点覆盖集——V*的任何真子集都不是点覆盖集
(3) 最小点覆盖集(或最小点覆盖)——顶点数最少的点覆盖集
(4) 点覆盖数——α0(G)——最小点覆盖的元素个数

(4)边覆盖集

设G=<V,E>,E*属于E,
(1) E* 为边覆盖集——任意v属于V,存在e属于E,使得v与e关联
(2) E* 为极小边覆盖——E* 的真子集不是边覆盖
(3) 最小边覆盖——边数最少的边覆盖
(4) 边覆盖数α1——最小边覆盖中元素个数

(5)匹配

设G=<V,E>, E*属于E,
(1) 匹配(边独立集)E*——E*中各边均不相邻
(2) 极大匹配E*——E*中不能再加其他边了
(3) 最大匹配——边数最多的匹配
(4) 匹配数——最大匹配中的边数,记为β1 

(6)匹配及其他概念

设M为G中一个匹配.
(1) vi 与vj 被M匹配——(vi,vj)属于M
(2) v为M饱和点——有M中边与v关联
(3) v为M非饱和点——无M中边与v关联
(4) M为完美匹配——G中无M非饱和点
(5) M的交错路径——从M与E-M中交替取边构成的G中路径
(6) M的可增广交错路径——起、终点都是M非饱和点的交错路径
(7) M的交错圈——由M与E-M中的边交替出现构成的G中圈

(7)完美匹配

设G=<V1,V2,E>为二部图,|V1|≤|V2|,M是G中
最大匹配,若V1中顶点全是M饱和点,则称M为G中完备匹
配. 
|V1|=|V2| 时完备匹配变成完美匹配.


我的理解:

独立集:
    独立集是指图的顶点集的一个子集,该子集的导出子图不含边.如果一个独立集不是任何一个独立集的子集, 那么称这个独立集是一个极大独立集.一个图中包含顶点数目最多的独立集称为最大独立集。最大独立集一定是极大独立集,但是极大独立集不一定是最大的独立集。
支配集:
    与独立集相对应的就是支配集,支配集也是图顶点集的一个子集,设S 是图G 的一个支配集,则对于图中的任意一个顶点u,要么属于集合s, 要么与s 中的顶点相邻。在s中除去任何元素后s不再是支配集,则支配集s是极小支配集。称G的所有支配集中顶点个数最少的支配集为最小支配集,最小支配集中的顶点个数成为支配数。
最小点的覆盖:
    最小点的覆盖也是图的顶点集的一个子集,如果我们选中一个点,则称这个点将以他为端点的所有边都覆盖了。将图中所有的边都覆盖所用顶点数最少,这个集合就是最小的点的覆盖。
最大团:
    图G的顶点的子集,设D是最大团,则D中任意两点相邻。若u,v是最大团,则u,v有边相连,其补图u,v没有边相连,所以图G的最大团=其补图的最大独立集。

支配集(顶点集S):对于V - S 中任何一个点v ,都有S 中的某个点u , 使得( u , v) ∈E

点覆盖(顶点集S):对于任意的边e∈E,都存在顶点v∈S与e关联。(最小点覆盖的边数叫做点覆盖数)

边覆盖(边集D):对G的任意顶点v∈V,都存在边e∈D与v关联。(最小边覆盖的边数叫做边覆盖数)

点独立集(顶点集S):S中任意两个顶点都不相邻。(最大边独立集的边数叫做边独立集数)

边独立集(变集D):D中的任意两条边都不相邻。(最大边独立集的边数叫做边独立集数)

匹配:边独立集又叫做匹配。(最大匹配的边数叫做匹配数)

点覆盖数:覆盖所有边的顶点的最小个数

点独立数:互相不邻接的顶点的最大个数

边覆盖数:覆盖所有顶点的边的最小个数

边独立数:互相不邻接的边的最大个数


一些常用性质:


对于无向图:

最小点覆盖+最大独立集=顶点个数

最大团=补图的最大独立子集

关系1:给定图G = (V,E)无孤立点,则G的极大点独立集都是G的极小支配集。

关系2:G的点覆盖数 a与点独立集数 b满足: a + b = n。

关系3:G的边覆盖数 a与边独立集数 b满足: a + b = n。(边独立集数即匹配数)

关系3:给定图G = (V,E)无孤立点,|V | = n。M是G的匹配,W是G的边覆盖,则|M|≤|W|,等号成立时M是G的完美匹配而W是G的最小边覆盖。


对于二部图

最小点覆盖数 = 最大匹配数

最小路径覆盖 = 顶点数 – 最大(二分)匹配数 / 2(将每个点拆分为两个点、无一例外)


那么本题就可以利用上面的一个性质,即:最小路径覆盖(雷达所覆盖的图上的顶点) = 总共的顶点数目(代码中的countt计数变量)- 最大匹配数(Hungary算法返回值——二分图最大匹配数)/ 2。


最重要的两点,为何拆点,以及如何拆点。

为何拆点?

将该城市与周边的(四个方向)的城市连线,如图:(仍然请忽略有向性,可理解为,双向图就是无向图)



构成二分图的基本模式(一般意义上的、一般形式上的二分图)

如何拆点,图片里面就能看出来了。

好了,至此,题目出来了。

#include <iostream>

using namespace std;

#define MAXN 405
#define _clr(x) memset(x,0xff,sizeof(int)*MAXN)

int map[MAXN][MAXN];
int city[MAXN][MAXN];
int dire_row[4] = { -1100 };
int dire_column[4] = { 00, -11 };
int match1[MAXN], match2[MAXN];// the bipartite graph's two part...

int hungary(int m, int n, int mat[][MAXN], int* match1, int* match2){
    int s[MAXN], t[MAXN], p, q, ret = 0, i, j, k;
    for (_clr(match1), _clr(match2), i = 1; i<=m; ret += (match1[i++] >= 0))
    for (_clr(t), s[p = q = 0] = i; p <= q&&match1[i]<0; p++)
    for (k = s[p], j = 1; j<=n&&match1[i]<0; j++)
    if (mat[k][j] && t[j]<0){
        s[++q] = match2[j], t[j] = k;
        if (s[q]<0)
        for (p = j; p >= 0; j = p)
            match2[j] = k = t[j], p = match1[k], match1[k] = j;
    }
    return ret;
}

int main()
{
    int n, m;
    int t;
    cin >> t;
    while (t--)
    {
        cin >> n >> m;
        memset(map, 0sizeof(map));
        memset(city, 0sizeof(city));

        int countt = 0;//count of the citys

        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= m; j++)
            {
                char temp;
                cin >> temp;
                if (temp == '*') map[i][j] = ++countt;
            }
        }

        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= m; j++)
            {
                if (map[i][j])
                {
                    for (int k = 0; k < 4; k++)
                    {
                        int x = i + dire_row[k];
                        int y = j + dire_column[k];
                        if (map[x][y])
                            city[map[i][j]][map[x][y]] = 1;
                    }
                }
            }
        }
        cout << countt - (hungary(countt, countt, city, match1, match2) / 2) << endl;
    }
    return 0;
}

这种感觉真不错!学新知识,把它搞懂,然后应用。说实话,离散数学比以前构造的所有数学体系,对比起来,反差比我想象中的大。构造定义、证明起来,都跟“连续”数学,或称分析方法,变化很大!




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值