#P3319. 第2题-独立匹配
-
1000ms
Tried: 106
Accepted: 32
Difficulty: 4
所属公司 :
京东
时间 :2025年7月26日
-
算法标签>模拟
第2题-独立匹配
题解思路与方法
问题重述
给定一个无向图 G,定义边集 X 为独立匹配,需同时满足:
- 匹配性:X 中任意两条边不共享端点。
- 独立性:对于任意一条不在 X 中的边,它最多与 X 中的边共享一个端点(即不存在一条未选边与 X 中的两条边分别在两个端点处相连)。
每个查询给出一组边编号,判断该边集是否满足上述两条性质。
算法流程
-
读取边列表:用数组
edge[i] = (u_i, v_i)存储第 i 条边的两个端点。 -
遍历每个查询:
-
记查询边集大小为 c,边编号集合为 S。
-
匹配性检查:
-
用数组
used[v]标记顶点 v 是否已被 X 中的某条边占用。遍历 S 中的每条边 (u,v):- 若
used[u]或used[v]已为true,则有共享端点,直接判 “No”。 - 否则将
used[u]=used[v]=true。
- 若
-
-
独立性检查(仅在匹配性通过后进行):
- 对所有边编号 i∈/S,令 (u,v)=edge[i]。若
used[u] && used[v],说明该非选边与 X 中的两条边共享了两个端点,判 “No”。
- 对所有边编号 i∈/S,令 (u,v)=edge[i]。若
-
若两步均未触发冲突,则判 “Yes”。
-
复杂度分析
- 每个查询匹配性检查花费 O(c),独立性检查花费 O(m)。
- 共 q 次查询,总时间 O(∑c+q⋅m)≤O(qm+∑c)。
- 由于 m≤2000, q≤30,最坏约 6×104 次边扫描,极其高效。
代码实现
Python
import sys
def main():
input = sys.stdin.readline
n, m = map(int, input().split())
u_list = list(map(int, input().split()))
v_list = list(map(int, input().split()))
# 存储所有边
edges = [(u_list[i], v_list[i]) for i in range(m)]
q = int(input())
for _ in range(q):
data = list(map(int, input().split()))
c = data[0]
S = set(data[1:]) # 查询的边编号集合(1-based)
used = [False] * (n + 1) # 顶点占用标记
ok = True
# 匹配性检查
for idx in S:
u, v = edges[idx - 1]
if used[u] or used[v]:
ok = False
break
used[u] = used[v] = True
# 独立性检查
if ok:
for i in range(1, m + 1):
if i in S:
continue
u, v = edges[i - 1]
if used[u] and used[v]:
ok = False
break
print("Yes" if ok else "No")
if __name__ == "__main__":
main()
Java
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(in.readLine());
int n = Integer.parseInt(st.nextToken());
int m = Integer.parseInt(st.nextToken());
// 读取边端点
int[] u = new int[m], v = new int[m];
st = new StringTokenizer(in.readLine());
for (int i = 0; i < m; i++) u[i] = Integer.parseInt(st.nextToken());
st = new StringTokenizer(in.readLine());
for (int i = 0; i < m; i++) v[i] = Integer.parseInt(st.nextToken());
int q = Integer.parseInt(in.readLine());
while (q-- > 0) {
st = new StringTokenizer(in.readLine());
int c = Integer.parseInt(st.nextToken());
// 用 HashSet 存查询边编号
Set<Integer> S = new HashSet<>();
for (int i = 0; i < c; i++) {
S.add(Integer.parseInt(st.nextToken()));
}
boolean[] used = new boolean[n + 1];
boolean ok = true;
// 匹配性检查
for (int idx : S) {
int uu = u[idx - 1], vv = v[idx - 1];
if (used[uu] || used[vv]) {
ok = false;
break;
}
used[uu] = used[vv] = true;
}
// 独立性检查
if (ok) {
for (int i = 1; i <= m; i++) {
if (S.contains(i)) continue;
int uu = u[i - 1], vv = v[i - 1];
if (used[uu] && used[vv]) {
ok = false;
break;
}
}
}
System.out.println(ok ? "Yes" : "No");
}
}
}
C++
#include <bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
vector<int> u(m), v(m);
// 读入两行,分别是所有边的第一端点和第二端点
for(int i = 0; i < m; i++) cin >> u[i];
for(int i = 0; i < m; i++) cin >> v[i];
int q;
cin >> q;
while(q--){
int c;
cin >> c;
vector<bool> sel(m+1,false);
for(int i = 0, idx; i < c; i++){
cin >> idx;
sel[idx] = true; // 标记查询的边
}
vector<bool> used(n+1,false);
bool ok = true;
// 匹配性检查
for(int i = 1; i <= m && ok; i++){
if(!sel[i]) continue;
int uu = u[i-1], vv = v[i-1];
if(used[uu] || used[vv]){
ok = false;
break;
}
used[uu] = used[vv] = true;
}
// 独立性检查
if(ok){
for(int i = 1; i <= m; i++){
if(sel[i]) continue;
int uu = u[i-1], vv = v[i-1];
if(used[uu] && used[vv]){
ok = false;
break;
}
}
}
cout << (ok ? "Yes\n" : "No\n");
}
return 0;
}
题目内容
在一个无向图 G 中,独立匹配是图中的一个边集 X ,满足任何一条不属于 X 的边至多与 X 中的边共享一个端点,且 X 中任意两条边不共享端点。现在给出一个无向图和其中的一些边集,请你判断这些边集中哪一些是独立匹配。
输入描述
第一行有两个正整数 n,m(1<=n<=1000,1<=m<=2000) ,代表图中的点数和边数。
接下来的两行给出了一个 2∗m 的矩阵,矩阵中的每一列都代表图中的一条边的两个端点。
保证图中没有自环,即不存在一条边其两个端点相同。
接下来的一行中有一个正整数 q(1<=q<=30) ,代表询问的边集个数。
接下来 q 行,每行开头有一个正整数 c ,代表询问的边集大小,然后是 c 个 1 到 m 之间的正整数(含 1 和 m ),代表询问的边集中边的编号。
数字间两两有空格隔开。
输出描述
对于每个边集,若该边集是一个独立匹配,则输出 Yes 。否则输出 No 。
样例1
输入
6 6
1 2 3 4 5 6
2 3 4 5 6 1
3
2 1 2
2 2 4
2 3 6
输出
No
No
Yes