目录
一,拓扑图论
二,彼得森图
三,正则图
四,完全图
1,完全图
2,K7
3,K5
五,二分图
CodeForces 687A NP-Hard Problem
力扣 785. 判断二分图
六,完全二分图
1,完全二分图
2,K2,3
3,K3,3
七,广义子图、禁图表征
1,广义子图
2,广义子图定理
3,禁图表征
4,广义子图的二元关系
八,平面图
1,瓦格纳定理
拓扑图论研究曲面中的图嵌入、图的空间嵌入及作为拓扑空间的图,还研究图的浸入。
彼得森图由10个点组成,它不是平面图。
每个节点的度数都是k的无向图,称为k-正则图。
对于有向图,这个度数指的是入度和出度之和。
所以对于奇数k,k-正则图一定包含偶数个顶点。
判断算法:
//判断是不是k-正则图
static bool isCompleteGraph(int k, UndirectedGraphData &g)
{
for (auto &mi : g.adjaList) {
if (mi.second.size() != k)return false;
}
return true;
}
完全图指的是,对于不同的2点,点A到点B之间恰好有一条边,这样的简单图称为完全图。
n个点的无向完全图有n(n-1)/2条边,n个点的有向完全图有n(n-1)条边。
完全图用Kn表示。
无向完全图Kn是(n-1)正则图,有向完全图Kn是(2n-2)正则图。
比如无向完全图K7是6正则图:
PS:正则图、完全图很适合用来做动漫特效,比如K7就可以表示集齐七龙珠之后,阵法中的能量交换路径。
K5是彼得森图的广义子图。
一般我们研究二分图默认指的是无向图。
二分图是指,可以把图中的顶点分成两个集合,使得每个集合中的所有点之间都是互不相连的。
//判断是不是二分图, 节点编号是0到n-1
static bool isTwoDivGraph(int n, UndirectedGraphData &g) {
UnionDif unions(n);
for (auto e : g.edges) {
unions.find(e.a);
unions.find(e.b);
if (unions.inSame(e.a, e.b)) {
if (int(unions.getDif(e.a) + unions.getDif(e.b)) % 2)return false;
}
else {
unions.merge(e.a, e.b,1);
}
}
return true;
}
题目:
Description
Recently, Pari and Arya did some research about NP-Hard problems and they found the minimum vertex cover problem very interesting.
Suppose the graph G is given. Subset A of its vertices is called a vertex cover of this graph, if for each edge uv there is at least one endpoint of it in this set, i.e. or (or both).
Pari and Arya have won a great undirected graph as an award in a team contest. Now they have to split it in two parts, but both of them want their parts of the graph to be a vertex cover.
They have agreed to give you their graph and you need to find two disjoint subsets of its vertices A and B, such that both A and B are vertex cover or claim it's impossible. Each vertex should be given to no more than one of the friends (or you can even keep it for yourself).
Input
The first line of the input contains two integers n and m (2 ≤ n ≤ 100 000, 1 ≤ m ≤ 100 000) — the number of vertices and the number of edges in the prize graph, respectively.
Each of the next m lines contains a pair of integers ui and vi (1 ≤ ui, vi ≤ n), denoting an undirected edge between ui and vi. It's guaranteed the graph won't contain any self-loops or multiple edges.
Output
If it's impossible to split the graph between Pari and Arya as they expect, print "-1" (without quotes).
If there are two disjoint sets of vertices, such that both sets are vertex cover, print their descriptions. Each description must contain two lines. The first line contains a single integer k denoting the number of vertices in that vertex cover, and the second line contains kintegers — the indices of vertices. Note that because of m ≥ 1, vertex cover cannot be empty.
Sample Input
Input
4 2
1 2
2 3
Output
1
2
2
1 3
Input
3 3
1 2
2 3
1 3
Output
-1
题目大意就是把图分成二分图,如果不能的话,输出-1,如果能的话,分别输出2个部分的点的数量和点的标号。
代码:
#include
#include
using namespace std;
int list[100005];
int fenzu[100005];
int main()
{
int n, m;
cin >> n >> m;
int fx, fy;
memset(list, 0, sizeof(list));
memset(fenzu, 0, sizeof(fenzu));
int x, y;
int zu = 1;
while (m--)
{
cin >> x >> y;
if (list[x] == 0)
{
if (list[y] == 0)
{
fenzu[x] = zu;
fenzu[y] = zu;
list[x] = 1;
list[y] = 2;
zu++;
}
else
{
fenzu[x] = fenzu[y];
list[x] = 3 - list[y];
}
}
else
{
if (list[y] == 0)
{
fenzu[y] = fenzu[x];
list[y] = 3 - list[x];
}
else
{
fx = fenzu[x];
fy = fenzu[y];
if (fx == fy)
{
if (list[x] == list[y])
{
cout << "-1";
return 0;
}
}
else
{
if (list[x] == list[y])
{
for (int i = 1; i <= n; i++)
if (fenzu[i] == fy)
{
fenzu[i] = fx;
list[i] = 3 - list[i];
}
}
else for (int i = 1; i <= n; i++)
if (fenzu[i] == fy)fenzu[i] = fx;
}
}
}
}
int sum = 0;
for (int i = 1; i <= n; i++)if (list[i] == 1)sum++;
cout << sum << endl;
for (int i = 1; i <= n; i++)if (list[i] == 1)cout << i<<" ";
cout << endl << n - sum << endl;
for (int i = 1; i <= n; i++)if (list[i] != 1)cout << i << " ";
return 0;
}
可惜超时了,在test14超时了,test14估计是n和m非常大的了。
前面13个test都过了,想必这个代码是没有问题的,就是效率的问题。
很显然,主要的效率问题就是,在唯一的while循环里面,有1个从1到n的循环,只为了检索一些特定的目标出来。
所以很显然,我应该使用队列。
抛弃这个思想,用队列来做,其实很简单,只要记录每个点的所有邻居即可。
代码:
#include
#include
#include
#include
using namespace std;
vectorv[100005];
queueq;
int list[100005];
int main()
{
int n, m;
cin >> n >> m;
int x, y;
while (m--)
{
cin >> x >> y;
v[x].insert(v[x].end(),y);
v[y].insert(v[y].end(),x);
}
int k, key = 1;
memset(list, 0, sizeof(list));
vector< int >::iterator p;
while (1)
{
while (list[key])key++;
if (key > n)break;
q.push(key);
list[key] = 1;
while (!q.empty())
{
k = q.front();
q.pop();
for (p = v[k].begin(); p != v[k].end(); p++)
{
if (list[*p] == list[k])
{
cout << "-1";
return 0;
}
if (list[*p] == 0)
{
q.push(*p);
list[*p] = 3 - list[k];
}
}
}
}
int sum = 0;
for (int i = 1; i <= n; i++)if (list[i] == 1)sum++;
cout << sum << endl;
for (int i = 1; i <= n; i++)if (list[i] == 1)cout << i<<" ";
cout << endl << n - sum << endl;
for (int i = 1; i <= n; i++)if (list[i] != 1)cout << i << " ";
return 0;
}
表面上有3层循环,实际上外面的2层while循环是有限制的,合起来也就是大约n而已。
里面的循环就更不用说了,3层循环合起来也就是大约n而已。
所以这个算法还是很快的,果然AC了。
存在一个 无向图 ,图中有 n
个节点。其中每个节点都有一个介于 0
到 n - 1
之间的唯一编号。给你一个二维数组 graph
,其中 graph[u]
是一个节点数组,由节点 u
的邻接节点组成。形式上,对于 graph[u]
中的每个 v
,都存在一条位于节点 u
和节点 v
之间的无向边。该无向图同时具有以下属性:
graph[u]
不包含 u
)。graph[u]
不包含重复值)。v
在 graph[u]
内,那么 u
也应该在 graph[v]
内(该图是无向图)u
和 v
之间可能不存在一条连通彼此的路径。二分图 定义:如果能将一个图的节点集合分割成两个独立的子集 A
和 B
,并使图中的每一条边的两个节点一个来自 A
集合,一个来自 B
集合,就将这个图称为 二分图 。
如果图是二分图,返回 true
;否则,返回 false
。
示例 1:
输入:graph = [[1,2,3],[0,2],[0,1,3],[0,2]]
输出:false
解释:不能将节点分割成两个独立的子集,
以使每条边都连通一个子集中的一个节点与另一个子集中的一个节点。
示例 2:
输入:graph = [[1,3],[0,2],[1,3],[0,2]]
输出:true
解释:可以将节点分成两组: {0, 2} 和 {1, 3} 。
提示:
graph.length == n
1 <= n <= 100
0 <= graph[u].length < n
0 <= graph[u][i] <= n - 1
graph[u]
不会包含 u
graph[u]
的所有值 互不相同graph[u]
包含 v
,那么 graph[v]
也会包含 u
c++代码:
class Solution {
public:
bool isBipartite(vector>& graph) {
map>m;
for (int i = 0; i < graph.size(); i++)m[i] = graph[i];
UndirectedGraphData g(m);
return UndirectedGraph::isTwoDivGraph(m.size(), g);
}
};
rust代码:
struct Union{
fa:Vec,
dif:Vec,
}
impl Union{
fn find(& mut self,x:usize)->usize{
if self.fa[x] == x{
return x;
}
Union::find(self,self.fa[x]);
self.dif[x] += self.dif[self.fa[x]];
self.fa[x] = self.fa[self.fa[x]];
return self.fa[x];
}
fn in_same(& mut self,x:usize,y:usize)->bool{
return Union::find(self,x) == Union::find(self,y);
}
fn merge(& mut self,x:usize,y:usize,x_sub_y:i32)->(){
Union::find(self,x);
let fax = self.fa[x];
self.dif[fax] = x_sub_y - self.dif[x];
self.fa[fax] = y;
}
}
impl Solution {
pub fn is_bipartite(graph: Vec>) -> bool {
let mut fa=Vec::new();
let mut dif=Vec::new();
for i in 0..graph.len(){
fa.push(i);
dif.push(0);
}
let mut opt = Union{fa:fa,dif:dif};
for x in 0..graph.len(){
for y in graph[x].clone(){
let y = y as usize;
if opt.in_same(x, y){
if (opt.dif[x]+opt.dif[y])%2 == 0{
return false;
}
}else{
opt.merge(x, y, 1);
}
}
}
return true;
}
}
完全二分图是一种特殊的二分图,可以把图中的顶点分成两个集合,使得第一个集合中的所有顶点都与第二个集合中的所有顶点相连。
参考图论
广义子图也叫图子式。
待更新
https://zh.wikipedia.org/wiki/%E5%9B%BE%E5%AD%90%E5%BC%8F
禁图表征是一种图论专有的语法,该语法的形式是:
只有一个图不以某些图作为广义子图,那么这个图就满足某个性质。
这种定理,称为这种性质的禁图表征。
待更新
瓦格纳定理就是平面图的禁图表征:
当且仅当一个图不存在完全图K5和完全二分图K3,3的子式时,这个图才是平面图。