染色判定二分图
Graph Coloring I
题意:
判定无向连通图是不是二分图
如果是输出染色方案,否则输出奇环长和环上点的编号
思路:
显然如果是二分图输出染色方案即可,不是二分图一定存在奇环,不会存在
−
1
-1
−1 的情况
存在偶环二分图也是一样能染色成功的,必须是奇环才会有冲突
染色问题很好解决,
d
f
s
dfs
dfs 和
b
f
s
bfs
bfs 都可以很简单的实现
但是找奇环这个有点麻烦
在有向图中,tarjan算法 可以用来求强连通分量
这里求奇环也可以类似的实现
(其实这个题目就是个找奇环的,染色谁不会啊
code:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define mem(x, d) memset(x, d, sizeof(x))
#define eps 1e-6
using namespace std;
const int maxn = 1e6 + 9;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
vector <int> e[maxn], ans;
int st[maxn], top, color[maxn];
bool ok = 1;
void dfs(int x){
if(!ok) return;
st[++top] = x;
for(int to : e[x]){
if(!color[to]){
color[to] = color[x] == 1 ? 2 : 1;
dfs(to);
}
else if(ok && color[to] == color[x]){
ok = 0; int t;
do{
t = st[top--];ans.push_back(t);
}while(t != to);// 这里的找奇环是截至到to
return;
}
}
top--;// 细节,tarjan 算法中不会有pop掉这个操作
}
void work()
{
cin >> n >> m;
for(int i = 1; i <= m; ++i){
int x, y;cin >> x >> y;e[x].push_back(y);e[y].push_back(x);
}
color[1] = 1;
dfs(1);
if(ok){
cout << 0 << endl;for(int i = 1; i <= n; ++i) cout << color[i] - 1 << " ";
}
else {
cout << ans.size() << endl;for(auto x : ans) cout << x << " ";cout << endl;
}
}
int main()
{
ios::sync_with_stdio(0);
// int TT;cin>>TT;while(TT--)
work();
return 0;
}
图的遍历
题意:
无向图有
n
n
n 个点,从点
1
1
1 开始遍历,但是规定:按照每次“走两步”的方式来遍历整个图。可以发现按照每次走两步的方法,不一定能够遍历整个图,问最少加几条边,可以完整的遍历整个图。
思路:
虽然题目没说,但是这个图可能不连通
那么首先要保证图联通,那么需要
连
通
块
数
−
1
连通块数-1
连通块数−1 条边
可以发现,如果存在奇环,那么它就可以遍历所有的点
如果不存在,就加一条边形成奇环
code:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define eps 1e-6
using namespace std;
const int maxn = 3e5 + 9;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
vector <int> e[maxn];
int color[maxn];
int ans = 0, ok = 1;
void dfs(int x)
{
for(int i = 0; i < e[x].size(); ++i){
int to = e[x][i];
if(!color[to]){
color[to] = color[x] == 1 ? 2 : 1;
dfs(to);
}
else if(color[to] == color[x]) ok = 0;
// 注意这里不能 return,要继续染色
}
}
void work()
{
cin >> n >> m;
for(int i = 1, x, y; i <= m; ++i){
cin >> x >> y;e[x].push_back(y); e[y].push_back(x);
}
for(int i = 1; i <= n; ++i) if(!color[i])
{
++ans;
color[i] = 1;
dfs(i);
}
cout << ans - 1 + ok << endl;
}
int main()
{
ios::sync_with_stdio(0);
// int TT;cin>>TT;while(TT--)
work();
return 0;
}
小白月赛43–全体集合
题意:
给出
n
n
n 个点
m
m
m 条边 的无向图,给出
k
k
k 个点,这
k
k
k 个点上每个点都有一个人,每个人每回合能走到一个相邻的节点(不能停留不走),问:有没有可能在某一个回合,让这些人都集中在一个点?
思路:
如果图是二分图,需要所有人在同一个颜色的点上
如果不是二分图,那么就一定存在奇环,就一定可以
code:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define eps 1e-6
using namespace std;
const int maxn = 2e5 + 9;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m, k;
vector <int> e[maxn];
int color[maxn];
bool ok = 0;
void dfs(int x)
{
for(int to : e[x]) if(!color[to])
{
color[to] = color[x] == 1 ? 2 : 1;
dfs(to);
}
else if(color[to] == color[x]){
ok = 1;return;
}
}
void work()
{
cin >> n >> m >> k;
for(int i = 1, x, y; i <= m; ++i){
cin >> x >> y;
e[x].push_back(y);e[y].push_back(x);
}
color[1] = 1;
dfs(1);
set<int> se;
for(int i = 1, x; i <= k; ++i){
cin >> x;
se.insert(color[x]);
}
if(ok || se.size() == 1) cout << "YES";
else cout << "NO";
}
int main()
{
ios::sync_with_stdio(0);
// int TT;cin>>TT;while(TT--)
work();
return 0;
}
二分图最大匹配
棋盘覆盖
题意:
思路:
code: