首先明确几个概念
设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] = { -1, 1, 0, 0 }; int dire_column[4] = { 0, 0, -1, 1 }; 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, 0, sizeof(map)); memset(city, 0, sizeof(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; } |
这种感觉真不错!学新知识,把它搞懂,然后应用。说实话,离散数学比以前构造的所有数学体系,对比起来,反差比我想象中的大。构造定义、证明起来,都跟“连续”数学,或称分析方法,变化很大!