题目
题意
有多个点,一次输入两个点和一个操作,操作为0时,两个点都是白色,操作为1,两点一黑一白,为2时两点全为黑,问你最多有几个黑点。
思路
输入时给两点染上对应的颜色,同时判断是否已经染色,如果该点已经染色那么就不合法,如果需要其中一个染色但是不确定是那个,就先连接这两点,等dfs判断,因为没有染色的点会形成多个连通块,等统计完已经染色的数量,就可以同意处理,在对连通块染色时同时进行判断是否合法。最后如果合法输出已经染色的数量+最少需要的连通块内染色数量
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 200010;
int n, m;
int ne[N << 1], e[N << 1], he[N], idx;//图的组件
int co[N];//颜色
int wh, ba;//黑色和白色
void link(int a, int b)
{
e[idx] = b, ne[idx] = he[a], he[a] = idx++;
}
int dfs(int u, int c)
{
if (~co[u])//已经染色了
return co[u] == c;//判断是否合法
co[u] = c;//染色
c == 1 ? ++ba : ++wh;//记录黑色还是白色
for (int i = he[u]; ~i; i = ne[i])
{
int v = e[i];
if (!dfs(v, c ^ 1))//换个颜色继续染
return 0;//不合法QWQ
}
return 1;//所有的点都能合法的染色
}
int check()
{
for (int u = 1; u <= n; u++)//先看已经染色合不合法QWQ
{
if (~co[u])
{
for (int i = he[u]; ~i; i = ne[i])
{
int j = e[i];
if (!dfs(j, co[u] ^ 1))
return -1;
}
}
}
int res = 0;//记录一共染了多少点
for (int i = 1; i <= n; i++)
if (co[i] == 1)//已经染了
++res;
for (int i = 1; i <= n; i++)
{
if (!~co[i])//没染就给它染上
{
ba = wh = 0;
if (!dfs(i, 0))//合不合法
return -1;
res += min(ba, wh);//合法情况下看是染成黑色用的点少还是白色用的点少
}
}
return res;//可以返回染的点数了0_0
}
int main()
{
int f = 1;
cin >> n >> m;
memset(he, -1, sizeof he);//基操
memset(co, -1, sizeof co);
while (m--)
{
int a, b, op;
cin >> a >> b >> op;
if (op == 0)//都不染色标为白色
{
if (co[a] == 1 || co[b] == 1)//如果有个点已经染了
f = 0;//不合法QWQ
co[a] = co[b] = 0;//标记不染色
}
else if (op == 2)//都染色
{
if (co[a] == 0 || co[b] == 0)//如果有个点不染色
f = 0;//不合法QWQ
co[a] = co[b] = 1;//标记都染色
}
else
link(a, b), link(b, a);//如果不确定染不染色,就先连起来等一会的check
}
if (!f)
{
cout << "impossible" << endl;
}
else
{
int ans = check();//获取结果
if (~ans)
cout << ans << endl;
else
cout << "impossible" << endl;
}
}