输入样例:
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
输出样例:
No
Yes
Yes
No
No
解题思路:
要判断所给出的点是否可以覆盖所有边的顶点,可以按这种要求模拟,将每次询问的所有点都遍历它所接壤的边,判断总边数是否被这些顶点都覆盖了,但这种时间复杂度很大,达到O(k·n·n),最坏10^10,直接超时。但是如果反过来考虑,判断所有的边的两点是否在给定的一行询问中都存在,直接遍历边即可,时间复杂度为O(k·n),最坏10^6,不会超时。
在储存边时,因为程序中只是用到到了两边的顶点,并没有要求将边相连,或者遍历边,所以并不需要将边以邻接表存储,可以只用一个类存储所有边的顶点即可。
Java代码:(通过点判断边)超时
import java.io.*;
import java.util.Arrays;
public class Main {
static StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
public static int ini() throws IOException {
st.nextToken();
return (int)st.nval;
}
static int N = 10005, idx;
static int []e = new int[N];
static int []ne = new int[N];
static int []h = new int[N];
public static void main(String[] args) throws IOException {
int n = ini(), m = ini();
boolean [][]vis = new boolean[n][n];
Arrays.fill(h, -1);
int sum = 0;
while(m-- > 0) {
int a = ini(), b = ini();
add(a, b);
add(b, a);
sum += 2;
}
int q = ini();
while(q-- > 0) {
int k = ini();
int res = 0;
while(k-- > 0) {
int t = ini(); // 每输入一点,累加它所有相连的边
for(int i = h[t]; i != -1; i = ne[i]) {
int j = e[i];
if(!vis[t][j]) { // 将还没有标记过的边相加
vis[t][j] = vis[j][t] = true;
res += 2;
}
}
}
if(res == sum) System.out.println("Yes"); // 判断是否与总边数相等
else System.out.println("No");
for(int i = 0; i < n; i++) Arrays.fill(vis[i], false); // 更新标记数组
}
}
public static void add(int a, int b) {
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
}
Java代码:(通过邻接表的边判断点)AC
import java.io.*;
import java.util.Arrays;
public class Main {
static StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
public static int ini() throws IOException {
st.nextToken();
return (int)st.nval;
}
static int N = 10005, idx;
static int []e = new int[N];
static int []ne = new int[N];
static int []h = new int[N];
public static void main(String[] args) throws IOException {
int n = ini(), m = ini();
Arrays.fill(h, -1);
for(int i = 0; i < m; i++) {
int a = ini(), b = ini();
add(a, b);
add(b, a);
}
int q = ini();
boolean vis[] = new boolean[n];
while(q-- > 0) {
int k = ini();
Arrays.fill(vis, false);
for(int i = 0; i < k; i++) // 将每个询问的点数标记出来
vis[ini()] = true;
boolean flag = true;
for(int i = 0; i < n; i++) { // 遍历所有边,判断是否任意两边在点集中都至少有一个存在
for(int t = h[i]; t != -1; t = ne[t]) {
int j = e[t];
if(!vis[j] && !vis[i]) {
flag = false;
System.out.println("No");
break;
}
}
if(!flag) break;
}
if(flag) System.out.println("Yes");
}
}
public static void add(int a, int b) {
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
}
Java代码:(通过直接存储边判断点)AC
import java.io.*;
import java.util.Arrays;
public class Main {
static StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
public static int ini() throws IOException {
st.nextToken();
return (int)st.nval;
}
static class Node{
int a, b;
public Node(int a, int b) {
this.a = a;
this.b = b;
}
}
public static void main(String[] args) throws IOException {
int n = ini(), m = ini();
Node []node = new Node[m];
for(int i = 0; i < m; i++)
node[i] = new Node(ini(), ini());
int q = ini();
boolean vis[] = new boolean[n];
while(q-- > 0) {
int k = ini();
Arrays.fill(vis, false);
while(k-- > 0) vis[ini()] = true;
int i = 0;
for(i = 0; i < m; i++) // 直接遍历所有边
if(!vis[node[i].a] && !vis[node[i].b])
break;
if(i == m) System.out.println("Yes");
else System.out.println("No");
}
}
}