Lights G

Lights G ⁡ \operatorname{Lights\ G} Lights G

题目链接: luogu P2962 ⁡ \operatorname{luogu\ P2962} luogu P2962

题目

给出一张 n n n 个点 m m m 条边的无向图,每个点的初始状态都为 0 0 0

你可以操作任意一个点,操作结束后所有相邻的端点的状态都会改变,由 0 0 0 变成 1 1 1 或由 1 1 1 变成 0 0 0

你需要求出最少的操作次数,使得在所有操作完成之后所有 n n n 个点的状态都是 1 1 1

输入

第一行两个整数 n , m n, m n,m

之后 m m m 行,每行两个整数 a , b a, b a,b ,表示在点 a , b a, b a,b 之间有一条边。

输出

一行一个整数,表示最少需要的操作次数。

样例输入

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

样例输出

3 

数据范围

对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 35 , 1 ≤ m ≤ 595 , 1 ≤ a , b ≤ n 1\le n\le35,1\le m\le595, 1\le a,b\le n 1n35,1m595,1a,bn

思路

这道题是一道高斯消元题,不过是异或。
(半模板题吧,就是异或的高斯消元)

显而易见,每个点要么按一次,要么不按。
那方程就是按一个点对其它点有没有影响(或者说按每一个点对它是否有影响),那列出式子就很简单,预处理一下即可。

然后我们就异或的高斯消元。
然后如果没有自由元,我们就可以直接统计出答案。
但是如果有自由元,就要枚举每一个自由元的两种状态,然后不是自由元的部分就可以直接求出来,然后找到需要按的次数最少的一种按法。

然后就是答案了。

代码

#include<cstdio>
#include<algorithm>

using namespace std;

int n, m, x, y, ans, yes[37][37], choi[37];
bool have;

void gaosi() {
    
    //高斯化简异或式
	have = 1;
	for (int i = 1; i <= n; i++) {
    
    
		int cho = i;
		while (cho <= n && !yes[cho][i]) cho++;
		if (cho == n + 1) {
    
    
			have = 0;
			continue;
		}
		swap(yes[i], yes[cho]);
		for (int j = 1; j <= n; j++)
			if (i != j && yes[j][i]) {
    
    
				for (int k = i + 1; k <= n + 1; k++)
					yes[j][k] ^= yes[i][k];
				yes[j][i] = 0;
			}
	}
}

void dfs(int now, int num) {
    
    //dfs枚举自由元的每一种方式
	if (num >= ans) return ;
	if (!now) {
    
    
		ans = num;
		return ;
	}
	if (yes[now][now]) {
    
    //不是自由元,固定方式
		int add = 0;
		for (int i = now + 1; i <= n; i++)
			if (yes[now][i])
				add ^= choi[i];
		add ^= yes[now][n + 1];
		dfs(now - 1, num + add);
	}
	else {
    
    //是自由元,两个都dfs一次
		dfs(now - 1, num);
		choi[now] = 1;
		dfs(now - 1, num + 1);
		choi[now] = 0;
	}
}

int main() {
    
    
	scanf("%d %d", &n, &m);//读入
	for (int i = 1; i <= n; i++) {
    
    //初始化
		yes[i][i] = 1;
		yes[i][n + 1] = 1;
	}
	for (int i = 1; i <= m; i++) {
    
    
		scanf("%d %d", &x, &y);//读入
		yes[x][y] = 1;//记录
		yes[y][x] = 1;
	}
	
	gaosi();//高斯消元
	if (have) {
    
    //没有自由元
		for (int i = 1; i <= n; i++)
			ans += yes[i][n + 1];//直接找到答案
	}
	else {
    
    //有自由元
		ans = 10000;
		dfs(n, 0);//dfs枚举
	}
	
	printf("%d", ans);//输出
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43346722/article/details/108303894