4415 点的赋值(二分图 + 染色法)

本文探讨了如何在无向图中给每个节点赋奇数权值1、2或3,使得每条边两端点之和为奇数。通过DFS和BFS搜索策略,分析了二分图的性质,并提供了两种方法的代码实现。特别关注了超时问题的解决方案,包括数组模拟邻接表优化。
摘要由CSDN通过智能技术生成

1. 问题描述:

给定一个 n 个点 m 条边的无向无权图。点的编号为 1∼n,图中不含重边和自环。现在,请你给图中的每个点进行赋值,要求:每个点的权值只能是 1 或 2 或 3。对于图中的每一条边,其两端点的权值之和都必须是奇数,请问,共有多少种不同的赋值方法。由于结果可能很大,你只需要输出对 998244353 取模后的结果。

输入格式

第一行包含整数 T,表示共有 T 组测试数据,数据第一行包含两个整数 n,m,接下来 m 行,每行包含两个整数 u,v,表示点 u 和点 v 之间存在一条边。

输出格式

一个整数,表示不同赋值方法的数量对 998244353 取模后的结果

数据范围

输入样例:

2
2 1
1 2
4 6
1 2
1 3
1 4
2 3
2 4
3 4

输出样例:

4
0
来源:https://www.acwing.com/problem/content/description/4418/

2. 思路分析:

分析题目可以知道 n 个点 m 条边的无向图可能是不连通的,而且可以发现不同连通块之间是相互独立的,所以我们可以分别计算每一个连通块的方案数目,最终每一个连通块的方案数目相乘就是答案,由于任意两个端点的权值之和为奇数,所以肯定其中一个点的权值为奇数,另外一个点的权值为偶数,根据这个特点可以发现满足这个条件的无向图是二分图(可以将图中的点分为两大类且这些点分别属于两个不同的集合),而二分图中存在三个相互推导的充要条件,其中有一个充要条件是如果一个图是二分图那么染色法不存在矛盾,如果存在矛盾那么就不是二分图,方案数目就是 0,所以我们只需要计算二分图的方案数目即可,我们可以在遍历无向图节点的过程中对其染色,并且统计一下每个集合中点的数目 s1,s2,如果当前的图是二分图那么方案数目为 2 ^ s1 + 2 ^ s2(由于每个点奇数权值有两种所以方案数目为 2 ^ s1 或者 2 ^ s2),分别计算每一个连通块的方案数目乘起来就是答案: 

3. 代码如下:

python(dfs):超时:

import sys
from typing import List


class Solution:
    # s1, s2分别记录两个集合中点的数目
    s1 = s2 = None
    
    # 计算2的n次幂
    def power2(self, n: int, mod: int):
        res = 1
        for i in range(n):
            res = res * 2 % mod
        return res

    # u 表示当前节点, col 表示当前节点需要染成的颜色
    def dfs(self, u: int, c: int, color: List[int], g: List[List[int]]):
        color[u] = c
        if c == 1:
            self.s1 += 1
        else:
            self.s2 += 1
        for next in g[u]:
            # 下一个节点未染色那么将其染成 3 - c 的颜色, 发现矛盾返回False
            if color[next] == 0 and not self.dfs(next, 3 - c, color, g):
                return False
            # 已经染色判断是否存在矛盾
            elif color[next] != 0 and color[next] != 3 - c:
                return False
        return True

    def process(self):
        T = int(input())
        mod = 998244353
        while T > 0:
            res = 1
            n, m = map(int, input().split())
            g = [list() for i in range(n + 10)]
            for i in range(m):
                # 添加无向边
                a, b = map(int, input().split())
                g[a].append(b)
                g[b].append(a)
            color = [0] * (n + 10)
            for i in range(1, n + 1):
                if color[i] == 0:
                    self.s1 = self.s2 = 0
                    if not self.dfs(i, 1, color, g):
                        res = 0
                        break
                    # 计算当前集合的方案数目
                    res = res * (self.power2(self.s1, mod) + self.power2(self.s2, mod)) % mod
            print(res)
            T -= 1


if __name__ == '__main__':
    sys.setrecursionlimit(10 ** 5)
    Solution().process()

python(bfs):超时:

import collections
from typing import List


class Solution:
    s1 = s2 = None

    def power2(self, n: int, mod: int):
        res = 1
        for i in range(n):
            res = res * 2 % mod
        return res
    
    # bfs 染色
    def bfs(self, u: int, c: int, color: List[int], g: List[List[int]]):
        q = collections.deque([(u, c)])
        color[u] = c
        while q:
            p = q.popleft()
            c = p[1]
            if c == 1:
                self.s1 += 1
            else:
                self.s2 += 1
            for next in g[p[0]]:
                if color[next] != 0 and color[next] != 3 - c: return False
                elif color[next] == 0:
                    color[next] = 3 - c
                    q.append((next, 3 - c))
        return True

    def process(self):
        T = int(input())
        mod = 998244353
        while T > 0:
            res = 1
            n, m = map(int, input().split())
            g = [list() for i in range(n + 10)]
            for i in range(m):
                a, b = map(int, input().split())
                g[a].append(b)
                g[b].append(a)
            color = [0] * (n + 10)
            for i in range(1, n + 1):
                if color[i] == 0:
                    self.s1 = self.s2 = 0
                    if not self.bfs(i, 1, color, g):
                        res = 0
                        break
                    res = res * (self.power2(self.s1, mod) + self.power2(self.s2, mod)) % mod
            print(res)
            T -= 1


if __name__ == '__main__':
    Solution().process()

go:数组模拟邻接表:

package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
)

const N, M, mod = 300010, N * 2, 998244353

var (
	idx, s1, s2 int
	h, color    [N]int
	e, ne       [M]int
)

func add(a, b int) {
	e[idx] = b
	ne[idx] = h[a]
	h[a] = idx
	idx += 1
}

func dfs(u int, c int) bool {
	color[u] = c
	if c == 1 {
		s1 += 1
	} else {
		s2 += 1
	}
	for i := h[u]; i != -1; i = ne[i] {
		j := e[i]
		if color[j] == 0 && !dfs(j, 3-c) {
			return false
		} else if color[j] != 0 && color[j] != 3-c {
			return false
		}
	}
	return true
}

func power2(n int) int {
	res := 1
	for i := 0; i < n; i++ {
		res = res * 2 % mod
	}
	return res
}

func run(r io.Reader, w io.Writer) {
	in := bufio.NewReader(r)
	out := bufio.NewWriter(w)
	defer out.Flush()
	var T, n, m, a, b int
	for fmt.Fscan(in, &T); T > 0; T -= 1 {
		idx = 0
		fmt.Fscan(in, &n, &m)
        // 注意每次初始化的时候不要初始化整个数组, 否则也会 TLE
		for i := 0; i < n + 10; i++ {
			h[i] = -1
			color[i] = 0
		}
		for i := 0; i < m; i++ {
			fmt.Fscan(in, &a, &b)
			add(a, b)
			add(b, a)
		}
		res := 1
		for i := 1; i <= n; i++ {
			if color[i] == 0 {
				s1 = 0
				s2 = 0
				if !dfs(i, 1) {
					res = 0
					break
				}
				res = res * (power2(s1) + power2(s2)) % mod
			}
		}
		fmt.Fprintln(out, res)
	}
}

func main() {
	run(os.Stdin, os.Stdout)
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值