【JZOJ4672】Graph Coloring【模拟】【搜索】

题目大意:

题目链接:https://jzoj.net/senior/#main/show/4672
现在你有一张无向图包含nn个节点mm条边。最初,每一条边都是蓝色或者红色。每一次你可以将一个节点连接的所有边变色(从红变蓝,蓝变红)。
找到一种步数最小的方案,使得所有边的颜色相同。


思路:

记得以前做过费解的开关这道题,发现两道题蛮像的。
于是就用同样的思路去思考。
可以考虑先枚举其中一个点选择或不选择,以及最终要改成哪个颜色。如果我们已经把一条边上一个点修改了,那么如果要把这条边再次改回来,就只能把两另外一个点选择。但是你选择了这个点之后,又会有新的边被修改,然后依次类推。
所以,可以用一遍搜索来处理。如果经过若干次修改之后全部边都变成了一个颜色,那么就更新答案。


代码:

#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;

const int N=100010;
const int Inf=1e9;
int n,m,x,y,ans1,ans2,tot=1,head[N],father[N];
bool vis[N],used[N];
char ch;

struct edge
{
	int next,to,col;
}e[N*2],a[N*2],b[N*2];

int find(int x)
{
	return x==father[x]?x:father[x]=find(father[x]);
}

void add(int from,int to,int c)
{
	e[++tot].to=to;
	e[tot].col=c;
	e[tot].next=head[from];
	head[from]=tot;
}

bool check(int col)
{
	for (register int i=2;i<=tot;i++)
		if (a[i].col!=col) return 0;
	return 1;
}

int work1(int S,int col,int change)
{
	memset(vis,0,sizeof(vis));
	queue<int> q;
	vis[S]=1;
	int cnt=change;
	for (register int i=head[S];~i;i=a[i].next)
	{
		if (change)
			a[i].col^=1,a[i^1].col^=1;
		q.push(i);
	}
	while (q.size())
	{
		int j=q.front();
		q.pop();
		int u=a[j].to;
		vis[u]=1;
		if (a[j].col!=col)
		{
			cnt++;
			for (register int i=head[u];~i;i=a[i].next)
			{
				a[i].col^=1;
				a[i^1].col^=1;
				int v=a[i].to;
				if (vis[v]&&a[i].col!=col)
					return Inf;
				if (!vis[v]) q.push(i);
			}
		}
		else
		{
			for (register int i=head[u];~i;i=a[i].next)
			{
				int v=a[i].to;
				if (vis[v]&&a[i].col!=col)
					return Inf;
				if (!vis[v]) q.push(i);
			}
		}
	}
	return cnt;
}

int work2(int S,int col,int change)
{
	memset(vis,0,sizeof(vis));
	queue<int> q;
	vis[S]=1;
	int cnt=change;
	for (register int i=head[S];~i;i=b[i].next)
	{
		if (change)
			b[i].col^=1,b[i^1].col^=1;
		q.push(i);
	}
	while (q.size())
	{
		int j=q.front();
		q.pop();
		int u=b[j].to;
		vis[u]=1;
		if (b[j].col!=col)
		{
			cnt++;
			for (register int i=head[u];~i;i=b[i].next)
			{
				b[i].col^=1;
				b[i^1].col^=1;
				int v=b[i].to;
				if (vis[v]&&b[i].col!=col)
					return Inf;
				if (!vis[v]) q.push(i);
			}
		}
		else
		{
			for (register int i=head[u];~i;i=b[i].next)
			{
				int v=b[i].to;
				if (vis[v]&&b[i].col!=col)
					return Inf;
				if (!vis[v]) q.push(i);
			}
		}
	}
	return cnt;
}

int main()
{
	memset(head,-1,sizeof(head));
	scanf("%d%d",&n,&m);
	for (register int i=1;i<=n;i++)
		father[i]=i;
	for (register int i=1;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		father[find(x)]=find(y);
		while ((ch=getchar())&&ch!='R'&&ch!='B') ;
		if (ch=='R')
		{
			add(x,y,1);
			add(y,x,1);
		}
		else
		{
			add(x,y,0);
			add(y,x,0);
		}
	}
	memcpy(a,e,sizeof(e));
	memcpy(b,e,sizeof(e));
	for (register int i=1;i<=n;i++)
		if (!used[find(i)])
		{
			int fa=find(i);
			used[fa]=1;
			ans1+=min(work1(fa,1,0),work2(fa,1,1));
			if (ans1>=Inf) break;
		}
	memset(used,0,sizeof(used));
	memcpy(a,e,sizeof(e));
	memcpy(b,e,sizeof(e));
	for (register int i=1;i<=n;i++)
		if (!used[find(i)])
		{
			int fa=find(i);
			used[fa]=1;
			ans2+=min(work1(fa,0,0),work2(fa,0,1));
			if (ans2>=Inf) break;
		}
	if (ans1>=Inf&&ans2>=Inf) printf("-1");
		else printf("%d",min(ans1,ans2));
	return 0;
}
posted @ 2019-03-23 15:55  全OI最菜  阅读(140)  评论(0编辑  收藏  举报