【CF 662B】Graph Coloring
原创
©著作权归作者所有:来自51CTO博客作者Facico的原创作品,请联系作者获取转载授权,否则将追究法律责任
Description
现在你有一张无向图包含n个节点m条边。最初,每一条边都是蓝色或者红色。每一次你可以将一个节点连接的所有边变色(从红变蓝,蓝变红)。
找到一种步数最小的方案,使得所有边的颜色相同。
Solution
一开始看到这题,之前好像做过类似的题目,不过是在序列上的……
想了半天,啊,每个点最多只会被翻转一次:为什么呢,翻转两次会与不翻转的情况一样,翻转三次会与翻转一次的情况一样,最终翻奇数次的情况会与一次相同,翻偶数次的情况会与两次相同……所以最多只会翻转一次。
那么只要确定了一个点的翻转情况,和最终要染成的颜色,当前这个块的情况就可以确定了,很显然。
注意空间问题就好了。
Code
#include <cstdio>
#include <iostream>
#include <queue>
#include <memory.h>
#define fi first
#define se second
using namespace std;
const int maxN(1e5 + 10);
typedef int i_N[maxN];
typedef pair<int, int> p_ii;
int N, M;
namespace graph{
typedef pair<p_ii, int> EDGE;
i_N P, mark;
vector<int> adj[maxN];
EDGE edge[maxN];
void link(const int u, const int id){adj[u].push_back(id);}
int adj_node(const int u, const p_ii &E){return u == E.fi? E.se : E.fi;}
int bfs(const int sample, const int is_print){
queue<int> bfs_node;
memset(P, 0, sizeof(P));
int res(0);
for(int u = 1; u <= N; u++) if(!P[u]){
vector<int> tmp;
P[u] = -1;
mark[u] = 0;
bfs_node.push(u);
int C(0), C1(0);
while(!bfs_node.empty()){
const int u(bfs_node.front()); bfs_node.pop();
tmp.push_back(u);
C++;
C1 += mark[u];
for(int i = 0; i < (int)adj[u].size(); i++){
const int id(adj[u][i]);
const int v(adj_node(u, edge[id].fi)), color(edge[id].se), t((color != sample) ^ mark[u]);
if(!P[v]){
P[v] = u;
mark[v] = t;
bfs_node.push(v);
}else if(mark[v] != t){
if(is_print) cout << -1;
return 2e9;
}
}
}
res += min(C1, C - C1);
if(C1 > C - C1) for(int i = 0; i < (int)tmp.size(); i++) mark[tmp[i]] = 1 - mark[tmp[i]];
}
if(is_print){
cout << res << '\n';
for(int u = 1; u <= N; u++) if(mark[u]) cout << u << ' ';
}
return res;
}
}
int main(){
using namespace graph;
cin >> N >> M;
for(int i = 0; i < M; i++){
char ch;
cin >> edge[i].fi.fi >> edge[i].fi.se >> ch;
edge[i].se = ch == 'R';
link(edge[i].fi.fi, i);
link(edge[i].fi.se, i);
}
{
using namespace graph;
if(bfs(0, 0) < bfs(1, 0)) bfs(0, 1); else bfs(1, 1);
}
}