POJ.1753 Flip Game(高斯消元+枚举自由元 两次)

博客围绕POJ.1753 Flip Game问题展开,给定4*4矩阵,每个位置有开关状态,按动某位置会影响上下左右状态。通过为每个位置列线性方程构成方程组,用高斯消元求解,对存在自由元的情况枚举判断,取符合个数的最小值,比搜索方法更快。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:给你一个4*4的矩阵,每个位置对应着一个状态(开或者关),然后如果你按动某一个位置,那么上下左右位置都会被影响二改变状态,问最少经过几次达到全开或者全馆的状态。

分析:其实对于每一个位置都可以列一个线性方程,构成一个方程组,a[i][j]就是表示按动j位置时对i位置有影响,然后对于方程组用高斯消元求解,然后对于存在自由元的情况需要枚举判断符合的个数,最后取最小值。

然后 就比搜索跑得快多了
在这里插入图片描述

#include <cstdio>
#include <algorithm>
#include <cstring>
#include<string>
#include<iostream>
#define nmax 350
using namespace std;
const int inf = 0x3f3f3f3f;
int a[nmax][nmax];
int x[nmax];
int free_x[nmax];
char mp[nmax][nmax];
int equ, var;
int Gauss(){
	int max_r;
	int col = 0, num = 0;
	int k;
	for (int i = 0; i <= var; ++i) x[i] = free_x[i] = 0;
	for (k = 0; k < equ && col < var; k++, col++){
		max_r = k;
		for (int i = k + 1; i<equ; i++){
			if (abs(a[i][col])>abs(a[max_r][col])) max_r = i;
		}
		if (max_r != k){
			for (int j = k; j < var + 1; j++) swap(a[k][j], a[max_r][j]);
		}
		if (a[k][col] == 0){
			free_x[num++] = col;
			k--; continue;
		}
		for (int i = k + 1; i < equ; i++){
			if (a[i][col] != 0){
				for (int j = col; j < var + 1; j++){
					a[i][j] ^= a[k][j];;
				}
			}
		}
	}
	for (int i = k; i < equ; ++i){
		if (a[i][col] != 0) return -1;
	}
	if (k < var) return var - k;
	for (int i = var - 1; i >= 0; i--){
		x[i] = a[i][var];
		for (int j = i + 1; j < var; j++){
			x[i] ^= (a[i][j] && x[j]);
		}
	}
	return 0;
}
void init(){//初始化矩阵
	memset(a, 0, sizeof a);
	memset(x, 0, sizeof x);
	for (int i = 0; i < 4;i++)
	for (int j = 0; j < 4; j++){
		int pos = i * 4 + j;
		a[pos][pos] = 1;
		if (i>0) a[(i - 1) * 4 + j][pos] = 1;
		if (i < 3) a[(i + 1) * 4 + j][pos] = 1;
		if (j>0) a[i * 4 + j - 1][pos] = 1;
		if (j < 3) a[i * 4 + j + 1][pos] = 1;
	}
}
void enum_freex(int n, int & ans){//记接的个数 可放主函数
	//二进制枚举,相当于容斥的枚举,检查每个对应情况是否符合情况
	int num = (1 << (n));
	ans = 1e9 + 7;
	for (int i = 0; i < num; ++i){
		int cnt = 0;
		for (int j = 0; j < n; ++j){
			if (i&(1 << j)){
				cnt++;
				x[free_x[j]] = 1;
			}
			else x[free_x[j]] = 0;
		}
		for (int k = var - n - 1; k >= 0; --k){// 没有自由元的最下面一行
			int index = 0;
			for (index = k; k < var; index++){// 在当前行找到第一个非0自由元(如果存在的话)
				if (a[k][index]) break;
			}
			x[index] = a[k][var];
			for (int j = index + 1; j < var; ++j){// 向后依次计算出结果
				if (a[k][j]) x[index] ^= x[j];
			}
			cnt += x[index]; // 如果结果为1,则统计
		}
		ans = min(ans, cnt);
	}
}
int main(){
	var = equ = 16;
	for (int i = 0; i < 4; i++)
		cin >> mp[i];
	init();
	for (int i = 0; i < 4;i++)
	for (int j = 0; j < 4; j++){
		if (mp[i][j] == 'b') a[i * 4 + j][16] = 1;
		else a[i * 4 + j][16] = 0;
	}
	int ans1 = Gauss();
	//cout << ans1 << endl;
	if (ans1 == -1) ans1 = inf;//无解
	else if (ans1 == 0){//一个解
		int num = 0;
		for (int i = 0; i < 16; i++)
			num += x[i];
		ans1 = num;
	}
	else{
		int ans;
		enum_freex(4, ans);
		ans1 = ans;
	}
	//转化情况
	init();
	for (int i = 0; i < 4; i++)
	for (int j = 0; j < 4; j++){
		if (mp[i][j] == 'b') a[i * 4 + j][16] = 0;
		else a[i * 4 + j][16] = 1;
	}
	int ans2 = Gauss();
	//cout << ans2 << endl;
	if (ans2 == -1) ans2 = inf;
	else if (ans2 == 0){
		int num = 0;
		for (int i = 0; i < 16; i++)
			num += x[i];
		ans2 = num;
	}
	else{
		int ans;
		enum_freex(4, ans);
		ans2 = ans;
	}
	if (ans1 == inf&&ans1 == ans2)
		cout << "Impossible" << endl;
	else{
		//cout << ans1 << " " << ans2 << endl;
		cout << min(ans1, ans2) << endl;
	}
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值