CodeForces - 841D Leha and another game about graph 构造(dfs + 思维)

这篇博客介绍了如何解决Codeforces上的841D问题,涉及图论和 dfs 搜索策略。给定一个连通图,节点有0、1或-1的d值,目标是选择一组边,使所有节点的d值等于其度数的奇偶性。文章通过dfs来维护过程,分析节点状态变化,最终确定是否能达成目标条件,或者输出无法满足条件的情况。

传送门:Codeforces 841D

题意:给出一个连通图,并给每个点赋一个d值0或1或-1,要求选出一个边的集合,使得所有的点i要么d[i] == -1,要么

dgree[i] % 2 == d[i],dgree[i]代表i结点的度数。

思路:首先我们考虑一条边都不选的情况,此时所有d[i] == 0的i都满足了题目要求,此时如果有d[i] == 1的点,我们就要加一条边,我们考虑用dfs维护这个过程,在dfs序形成的搜索树上,若对于某个节点u,其子节点v有d[v] == 1,那么我们就将u和v之间的边的选取状态取反,即原来选变成不选,不选变成选,然后令d[v] = 0,代表该节点已经满足题意(你愿意赋值成其他值也无所谓,注意别弄混就行),若d[u] != -1,那么d[u]取反(因为u增加了一度,满足题意的状态肯定变化),可以发现,对于每个点,都能通过改变其和其父节点之间边的选取状态而改变其满足题意的状态,只有根节点没有父节点而例外,因此dfs完成后,如果根节点的状态为d[root] == 1,那么我们就要再选取一个d[i] == -1的结点i,将其到父节点的路径上所有边的选取状态取反,这样只会改变i和root结点的度数,路径上其他结点的度数并不受影响(因此满足题意的状态也不会改变),而对i来说度数改变是无所谓的,因此这样就只将d[root]变成了0。

那么什么时候输出-1呢,显然当不存在d[i] == -1的i并且d[root] == 1的时候,我们就无力回天了。

代码:

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
typedef pair<int,int> P;
const int MAXN = 300010;
vector<P> mp[MAXN];
P fa[MAXN];
int d[MAXN];
bool book[MAXN], select[MAXN];
void dfs(int u, int pre)
{
	int v, id;
	for(int i = 0; i < mp[u].size(); i++)
	{
		v = mp[u][i].first;
		id = mp[u][i].second;
		if(v == pre || book[v]) continue;
		book[v] = 1;
		fa[v] = P(u, id);
		dfs(v, u);
		if(d[v] == 1)
		{
			d[v] = 0;
			select[id] ^= 1;
			if(d[u] != -1)
			d[u] ^= 1;
		}
	}
}
int main()
{
	int n, m, u, v;
	int node = 0, ans = 0;
	cin >> n >> m;
	for(int i = 1; i <= n; i++)
	{
		scanf("%d", d + i);
		if(d[i] == -1) node = i;
	}
	for(int i = 1; i <= m; i++)
	{
		scanf("%d %d", &u, &v);
		mp[u].push_back(P(v, i));
		mp[v].push_back(P(u, i));
	}
	dfs(1, -1);
	if(d[1] == 1)
	{
		if(node == 0)
		{
			cout << -1;
			return 0;
		}
		while(node != 1)
		{
			select[fa[node].second] ^= 1;
			node = fa[node].first;
		}
	}
	for(int i = 1; i <= m; i++)
	if(select[i])
	ans++;
	cout << ans << endl;
	for(int i = 1; i <= m; i++)
	if(select[i])
	printf("%d ", i);
 	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值