A1134. Vertex Cover

本文探讨了图论中的顶点覆盖问题,通过给定的图和顶点集,判断该集合是否为有效的顶点覆盖。介绍了如何利用哈希表进行高效验证,并附带示例代码。

A vertex cover of a graph is a set of vertices such that each edge of the graph is incident to at least one vertex of the set. Now given a graph with several vertex sets, you are supposed to tell if each of them is a vertex cover or not.

Input Specification:

Each input file contains one test case. For each case, the first line gives two positive integers N and M (both no more than 1), being the total numbers of vertices and the edges, respectively. Then M lines follow, each describes an edge by giving the indices (from 0 to N1) of the two ends of the edge.

After the graph, a positive integer K (≤ 100) is given, which is the number of queries. Then K lines of queries follow, each in the format:

Nv​​ v[1v[2]v[Nv​​]

where Nv​​ is the number of vertices in the set, and v[i]'s are the indices of the vertices.

Output Specification:

For each query, print in a line Yes if the set is a vertex cover, or No if not.

Sample Input:

10 11
8 7
6 8
4 5
8 4
8 1
1 2
1 4
9 8
9 1
1 0
2 4
5
4 0 3 8 4
6 6 1 7 5 4 9
3 1 8 4
2 2 8
7 9 8 7 6 5 4 2

Sample Output:

No
Yes
Yes
No
No

#include<iostream>
#include<cstdio>
using namespace std;
int N, M, edge1[20001], edge2[20001], pt = 0;
int hashV[20001] = {0};
int main(){
    scanf("%d%d",&N, &M);
    for(int i = 0; i < M; i++){
        scanf("%d%d", &edge1[i], &edge2[i]);
    }
    int K;
    scanf("%d", &K);
    for(int i = 0; i < K; i++){
        int Nv, tag = 1;
        scanf("%d", &Nv);
        fill(hashV, hashV + N, 0);
        for(int j = 0; j < Nv; j++){
            int vv;
            scanf("%d", &vv);
            hashV[vv] = 1;
        }
        for(int j = 0; j < M; j++){
            if(hashV[edge1[j]] == 0 && hashV[edge2[j]] == 0){
                tag = 0;
                break;
            }
        }
        if(tag == 0)
            printf("No\n");
        else printf("Yes\n");
    }
    cin >> N;
    return 0;
}
View Code

 

总结:

1、题意:给出一个图,然后给出一组点的集合,问这组点能否满足:图中任意一条边均包含至少一个集合中的点。

2、属于模拟题,不需要按照往常存储图的方法(邻接表、邻接矩阵)存储,只需要将每一条边存下来方便遍历即可。可按照edge1[N], edge2[N]的方式存储边。遍历时顺序遍历即可。查看点是否存在,可以将集合中的点都存在哈希表中。

 

转载于:https://www.cnblogs.com/zhuqiwei-blog/p/9553806.html

### 从 3 - SAT 到顶点覆盖问题的多项式时间映射归约证明 #### 1. 3 - SAT 到顶点覆盖问题的归约思路 3 - SAT(3 合取范式可满足性问题)是指给定一个由子句组成的布尔公式,每个子句包含恰好 3 个文字,判断是否存在一个变量赋值使得整个公式为真。顶点覆盖问题是指给定一个图 \(G=(V, E)\) 和一个整数 \(k\),判断是否存在一个大小为 \(k\) 的顶点子集 \(C\subseteq V\),使得图中的每一条边至少有一个端点在 \(C\) 中。 为了将 3 - SAT 问题归约到顶点覆盖问题,需要从给定的 3 - SAT 布尔公式 \(\varphi\) 构造一个图 \(G\),并且证明存在一个满足 \(\varphi\) 的赋值当且仅当图 \(G\) 有一个大小为 \(k\) 的顶点覆盖。 #### 2. 从布尔公式 \(\varphi\) 构造图 \(G\) 给定布尔公式 \(\varphi = (\neg x_1 \vee \neg x_1 \vee x_2) \wedge (\neg\neg\neg x_1 \vee \neg\neg\neg x_2 \vee \neg\neg\neg x_2) \wedge (\neg^8 x_3)\)(其中 \(\neg^n\) 表示 \(n\) 个否定符号,偶数个否定符号相当于没有否定,奇数个否定符号相当于一个否定),化简后为 \(\varphi = (\neg x_1 \vee x_2) \wedge (\neg x_1 \vee \neg x_2) \wedge (x_3)\) 构造图 \(G\) 的方法如下: - **变量装置**:对于每个变量 \(x_i\),创建两个顶点 \(x_i\) 和 \(\neg x_i\),并在它们之间连接一条边。这表示变量 \(x_i\) 只能取真(选择 \(x_i\))或假(选择 \(\neg x_i\))。 - **子句装置**:对于每个子句 \(C_j\),创建一个包含三个顶点的三角形,每个顶点对应子句中的一个文字。 - **连接边**:将子句装置中的顶点与变量装置中对应的顶点连接起来。 对于公式 \(\varphi\): - 变量有 \(x_1\),\(x_2\),\(x_3\),所以有三个变量装置,即三条边 \((x_1, \neg x_1)\),\((x_2, \neg x_2)\),\((x_3, \neg x_3)\)。 - 子句有三个: - \(C_1 = \neg x_1 \vee x_2\),创建一个包含 \(\neg x_1\) 和 \(x_2\) 以及一个中间顶点 \(v_{C_1}\) 的三角形。 - \(C_2 = \neg x_1 \vee \neg x_2\),创建一个包含 \(\neg x_1\) 和 \(\neg x_2\) 以及一个中间顶点 \(v_{C_2}\) 的三角形。 - \(C_3 = x_3\),创建一个包含 \(x_3\) 以及两个中间顶点 \(v_{C_3}^1\) 和 \(v_{C_3}^2\) 的三角形。 - 连接边:将子句装置中的 \(\neg x_1\),\(x_2\),\(\neg x_2\),\(x_3\) 与变量装置中对应的顶点连接起来。 #### 3. 确定顶点覆盖的大小 \(k\) 如果布尔公式 \(\varphi\) 有 \(n\) 个变量和 \(m\) 个子句,那么顶点覆盖的大小 \(k=n + 2m\)。这里 \(n = 3\)(变量 \(x_1\),\(x_2\),\(x_3\)),\(m = 3\)(三个子句),所以 \(k=3+2\times3 = 9\)(原问题说的八个顶点覆盖可能有误,通常按照此方法构造是 \(n + 2m\) ),但按照正常推理来继续分析如何找顶点覆盖 #### 4. 从满足赋值得到顶点覆盖 假设存在一个满足 \(\varphi\) 的赋值。例如,一个可能的满足赋值是 \(x_1 = false\),\(x_2 = false\),\(x_3 = true\)。 - 对于变量装置: - 因为 \(x_1 = false\),选择 \(\neg x_1\) 加入顶点覆盖。 - 因为 \(x_2 = false\),选择 \(\neg x_2\) 加入顶点覆盖。 - 因为 \(x_3 = true\),选择 \(x_3\) 加入顶点覆盖。 - 对于子句装置: - 对于子句 \(C_1=\neg x_1 \vee x_2\),由于 \(\neg x_1\) 已经在顶点覆盖中,为了覆盖子句装置中的三角形的边,选择另外两个顶点中的一个,比如选择 \(v_{C_1}\) 加入顶点覆盖。 - 对于子句 \(C_2=\neg x_1 \vee \neg x_2\),因为 \(\neg x_1\) 和 \(\neg x_2\) 都在顶点覆盖中,为了覆盖三角形的边,选择中间顶点 \(v_{C_2}\) 加入顶点覆盖。 - 对于子句 \(C_3 = x_3\),因为 \(x_3\) 在顶点覆盖中,选择 \(v_{C_3}^1\) 和 \(v_{C_3}^2\) 中的一个,比如 \(v_{C_3}^1\) 加入顶点覆盖 #### 证明归约的正确性 - **如果存在满足 \(\varphi\) 的赋值,则图 \(G\) 有大小为 \(k\) 的顶点覆盖**:根据满足赋值选择变量装置中的顶点,由于每个子句都为真,所以每个子句装置中至少有一个顶点与变量装置中已选的顶点相连,再选择子句装置中剩余的边所需的顶点,就可以得到一个大小为 \(k\) 的顶点覆盖。 - **如果图 \(G\) 有大小为 \(k\) 的顶点覆盖,则存在满足 \(\varphi\) 的赋值**:顶点覆盖中变量装置的顶点选择确定了变量的赋值,由于所有边都被覆盖,所以每个子句装置中的边都被覆盖,这意味着每个子句都为真,从而存在满足 \(\varphi\) 的赋值。 ### 代码实现(Python 示例,用于构造图) ```python import networkx as nx import matplotlib.pyplot as plt # 布尔公式对应的子句 clauses = [('!x1', 'x2'), ('!x1', '!x2'), ('x3')] variables = ['x1', 'x2', 'x3'] # 创建图 G = nx.Graph() # 添加变量装置 for var in variables: G.add_edge(var, f'!{var}') # 添加子句装置和连接边 clause_vertices = [] for i, clause in enumerate(clauses): clause_vertex = f'C{i}' clause_vertices.append(clause_vertex) G.add_node(clause_vertex) for literal in clause: G.add_edge(clause_vertex, literal) # 绘制图 pos = nx.spring_layout(G) nx.draw(G, pos, with_labels=True) plt.show() ``` ### 总结 通过上述步骤,我们完成了从 3 - SAT 问题到顶点覆盖问题的多项式时间映射归约。从布尔公式构造图,并且说明了如何从满足赋值得到图的顶点覆盖。归约的正确性保证了两个问题的可解性等价。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值