若能将无向图 G=(V,E) G = ( V , E ) 画在平面上使得任意两条无重合顶点的边不相交,则称 G G 是平面图。判定一个图是否为平面图的问题是图论中的一个重要问题。现在假设你要判定的是一类特殊的图,图中存在一个包含所有顶点的环,即存在哈密顿回路。
输入文件的第一行是一个正整数 T T ,表示数据组数(每组数据描述一个需要判定的图)。接下来从输入文件第二行开始有 T T 组数据,每组数据的第一行是用空格隔开的两个正整数 N N 和 M M ,分别表示对应图的顶点数和边数。紧接着的 M M 行,每行是用空格隔开的两个正整数 u u 和 v v ( 1<=u,v<=n 1 <= u , v <= n ),表示对应图的一条边 (u,v) ( u , v ) ,输入的数据保证所有边仅出现一次。每组数据的最后一行是用空格隔开的 N N 个正整数,从左到右表示对应图中的一个哈密顿回路: V1,V2,…,VN V 1 , V 2 , … , V N ,即对任意 i≠j i ≠ j 有 Vi≠Vj V i ≠ V j 且对任意 1<=i<=n−1 1 <= i <= n − 1 有 (Vi,Vi−1)∈E ( V i , V i − 1 ) ∈ E 及 (V1,Vn)∈E ( V 1 , V n ) ∈ E 。输入的数据保证 100% 100 % 的数据满足 T<=100,3<=N<=200,M<=10000 T <= 100 , 3 <= N <= 200 , M <= 10000 。
包含 T T 行,若输入文件的第 i i 组数据所对应图是平面图,则在第 i i 行输出 YES Y E S ,否则在第 i i 行输出 NO N O ,注意均为大写字母
2
6 9
1 4
1 5
1 6
2 4
2 5
2 6
3 4
3 5
3 6
1 4 2 5 3 6
5 5
1 2
2 3
3 4
4 5
5 1
1 2 3 4 5
NO
YES
如果没有哈密顿回路这个条件这道题显然十分不可做(据Rockdu Dark♂ lao说是判子图)。
不过已经有了一个环, 我们只需要考虑非环边。 显然每条非环边可以接在外面或里面, 所以每两条可能相交的边的编号我们连4条边, 然后一发 tarjan t a r j a n 即可AC…
不过这道题有个剪枝:一个平面图的总边数 m m 一定是 ≤n×3−6 ≤ n × 3 − 6 的。
证明:根据欧拉公式 n−m+r=2 n − m + r = 2 ( n n 个顶点, m m 条边, r r 个面),和 3×r=2×m 3 × r = 2 × m (每条边被两个面共享,每个面由三条边构成)可得。
所以那些边有几千条的根本不用跑…
代码如下:
#include
#include
#include
#include
#include
#include
#define R register
#define IN inline
#define W while
#define gc getchar()
#define ME 40050
#define MX 510
template <class T>
IN void in(T &x)
{
x = 0; R char c = gc;
W (!isdigit(c)) c = gc;
W (isdigit(c))
x = (x << 1) + (x << 3) + c - 48, c = gc;
}
int T, dot, line, col, cnt, ecnt, top, arr;
int head[ME], dfn[ME], low[ME], sta[ME];
int loop[ME], pos[MX], bel[ME];
bool mp[MX][MX], vis[ME];
struct Edge
{
int to, nex;
}edge[ME << 1];
struct EDGE
{
int from, to;
}e[ME << 1], oth[ME << 1], d1, d2;
IN void reset()
{
std::memset(mp, 0, sizeof(mp));
std::memset(low, 0, sizeof(low));
std::memset(bel, 0, sizeof(bel));
std::memset(dfn, 0, sizeof(dfn));
for (R int i = 1; i <= ecnt; ++i)
head[i] = head[i + line] = 0;
cnt = ecnt = col = top = arr = 0;
}
IN void addedge(const int &from, const int &to)
{edge[++cnt] = {to, head[from]}; head[from] = cnt;}
IN bool check(const int &a, const int &b)
{
d1 = oth[a], d2 = oth[b];
if(d1.from > d1.to) std::swap(d1.from, d1.to);
if(d2.from > d2.to) std::swap(d2.from, d2.to);
if(d1.from > d2.from) std::swap(d1, d2);
if(d1.from == d2.from || d1.to == d2.to) return false;
if(d1.to > d2.from && d1.to < d2.to) return true; return false;
}
void tarjan(const int &now)
{
dfn[now] = low[now] = ++arr;
sta[++top] = now, vis[now] = true;
for (R int i = head[now]; i; i = edge[i].nex)
{
if(!dfn[edge[i].to]) tarjan(edge[i].to), low[now] = std::min(low[now], low[edge[i].to]);
else if(vis[edge[i].to]) low[now] = std::min(low[now], dfn[edge[i].to]);
}
if(dfn[now] == low[now])
{
R int tp = 0; ++col;
W (tp != now)
{
tp = sta[top--];
bel[tp] = col, vis[tp] = false;
}
}
}
int main(void)
{
in(T); int bd;
W (T--)
{
reset(); in(dot), in(line);
for (R int i = 1; i <= line; ++i) in(e[i].from), in(e[i].to);
for (R int i = 1; i <= dot; ++i)
{
in(loop[i]); pos[loop[i]] = i;
mp[loop[i]][loop[i - 1]] = true;
mp[loop[i - 1]][loop[i]] = true;
}
if(dot * 3 - 6 < line) goto err;
mp[loop[1]][loop[dot]] = true, mp[loop[dot]][loop[1]] = true;
for (R int i = 1; i <= line; ++i)
{
if(mp[e[i].from][e[i].to]) continue;
oth[++ecnt] = {pos[e[i].from], pos[e[i].to]};
}
for (R int i = 1; i < ecnt; ++i)
{
for (R int j = i + 1; j <= ecnt; ++j)
{
if(check(i, j))
{
addedge(i, j + line), addedge(i + line, j);
addedge(j + line, i), addedge(j, i + line);
}
}
}
bd = ecnt + line;
for (R int i = 1; i <= ecnt; ++i) if(!dfn[i]) tarjan(i);
for (R int i = 1 + line; i <= bd; ++i) if(!dfn[i]) tarjan(i);
for (R int i = 1; i <= ecnt; ++i)
if(bel[i] == bel[i + line]) goto err;
printf("YES\n"); continue; err: printf("NO\n");
}
}