版权声明:欢迎借鉴,谢绝抄搬。 https://blog.csdn.net/Gx_Man_VIP/article/details/88823266
题目大意:
一张n个节点m条边的无向图。
最初,每一条边都是蓝色或者红色。
每一次你可以将一个节点连接的所有边变色(从红变蓝,蓝变红)。
找到一种步数最小的方案,使得所有边的颜色相同。
没有自环的边
分析:
分别将边变为蓝色或者红色,然后取最优方案
因为每一个点最多改变一次,改变两次等于没有变化。
因此我们要将点分为两个集合 S 和 T,分别代表要改变的点和不需要改变的点。
然后类似于
的思想,
如果我们现在要将所有边变为红色,假设 u 和 v 之间有一条红色的边。如果要维持这种颜色,u 和 v 要属于同一个
集合(S 或者 T)。另一方面,如果这条边是蓝色,那么 u 和 v 就必须在两个不同的集合。
所以问题简化为了 0-1 图染色问题,可以用dfs解决。注意图不一定连通。
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <cstring>
#include <algorithm>
#define N 100005
using namespace std;
struct Node {
int To, nxt, col;
}e[N*2];
int use[N], tot[2], ls[N], n, m, cnt, usecol;
bool check;
void Addedge(int u, int v, int w)
{
e[++cnt].To = v, e[cnt].col = w, e[cnt].nxt = ls[u], ls[u] = cnt;
e[++cnt].To = u, e[cnt].col = w, e[cnt].nxt = ls[v], ls[v] = cnt;
}
void dfs(int x)
{
if (!check) return;
tot[use[x]]++;
for (int i = ls[x]; i; i = e[i].nxt)
{
int now = e[i].col ^ use[x] ^ usecol;
if (use[e[i].To] == -1)
{
use[e[i].To] = now;
dfs(e[i].To);
if (!check) return;
} else if (use[e[i].To] != now) { check = 0; return; }
}
}
// 红 1 蓝 0
int main()
{
scanf("%d %d", &n, &m);
int u, v; char w;
for (int i = 1; i <= m; i++)
{
scanf("%d %d %c", &u, &v, &w);
Addedge(u, v, (w == 'R'));
}
int ans1 = 0;
memset(use, 255, sizeof(use)); usecol = 1; check = 1;
for(int i = 1; i <= n; i++)
if (use[i] == -1)
{
tot[1] = tot[0] = 0; use[i] = 1;
dfs(i);
ans1 += min(tot[0], tot[1]);
if (!check) { ans1 = -1; break; }
}
int ans2 = 0;
memset(use, 255, sizeof(use)); usecol = 0; check = 1;
for(int i = 1; i <= n; i++)
if (use[i] == -1)
{
tot[1] = tot[0] = 0; use[i] = 1;
dfs(i);
ans2 += min(tot[0], tot[1]);
if (!check)
{
if (ans1 == -1) printf("-1\n"); else printf("%d\n", ans1);
return 0;
}
}
if (ans1 == -1) printf("%d\n", ans2); else printf("%d\n", min(ans1, ans2));
return 0;
}