【高斯消元 && 01矩阵】POJ - 3185 The Water Bowls

本文介绍了一种解决特定异或方程组的方法:通过构造方程组并使用高斯消元法来求解最小翻转次数问题。具体步骤包括建立方程组、进行高斯消元、枚举自由元所有情况以寻找最小解。

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

Step1 Problem:

给你 20 个数,只有 0 1。
每次让相邻的三个数翻转,结果全为0,最少需要翻转几次?

Step2 Ideas:

构造异或方程组,求出自由元,枚举自由元所有情况,找出最小解。

Step3 Code:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 25;
const int inf = 0x3f3f3f3f;
int data[N][N], lib[N];
int x[5] = {1, -1, 0};
bool ok(int x, int y)
{
    if(x >= 0 && x < 20 && y >= 0 && y < 20) return 1;
    else return 0;
}
int Gauss(int n)
{
    memset(lib, 0, sizeof(lib));
    int r = 0, rst = 0;
    for(int i = 0; i < n; i++)
    {
        for(int j = r; j < n; j++)//找第i列,从第r行开始,第一个非0行,和r行交换
        {
            if(!data[j][i]) continue;
            for(int k = 0; k <= n; k++)
                swap(data[r][k], data[j][k]);
            break;
        }
        if(!data[r][i]) continue;//没找到,自由元数量++
        for(int j = 0; j < n; j++)//利用r行,消去其他行。
        {
            if(j == r || !data[j][i]) continue;
            for(int k = 0; k <= n; k++)
                data[j][k] ^= data[r][k];
        }
        lib[i] = 1; r++; rst++;
    }
    int ans = 0;
    for(int i = 0; i < n; i++)
    {
        if(data[i][n]) {//如果第i行,data[i][n]不是0,其他全为0则无解。
            int flag = 0;
            for(int j = 0; j < n; j++)
            {
                if(data[i][j]) flag = 1;
            }
            if(!flag) return -1;//无解
        }
        if(!lib[i]) continue;//判断 xi 是不是自由元
        if(!data[i][i]) continue;
        ans += data[i][n];//求唯一解的答案
    }
    if(rst == n) return ans;//唯一解,直接返回
    else ans = inf;
    int unk = n - rst;//自由变元
    for(int k = 0; k < (1<<unk); k++)//枚举自由变元可能解的所有情况
    {
        int num = 0, cnt = 0;
        for(int i = 0; i < unk; i++) num += (k>>i)&1;//自由变元 为 1的数量
        for(int i = 0; i < rst; i++)//代回到前rst行
        {
            int t = 0;
            for(int j = 0, j1 = 0; j < n; j++) {
                if(!lib[j]) {//找到自由元
                    if(data[i][j]) t ^= (k>>j1)&1;
                    j1++;
                }
            }
            if(t != data[i][n]) cnt++;
        }
        ans = min(cnt+num, ans);//更新最小解
    }
    return ans;
}
int main()
{
    int n = 20;
    memset(data, 0, sizeof(data));
    for(int i = 0; i < n; i++)
    {
        scanf("%d", &data[i][n]);
        for(int k = 0; k < 3; k++)//data[i][n]只由 周围的三个决定
        {
            int tx = i, ty = i+x[k];
            if(ok(tx, ty)) {
                data[i][ty] = 1;
            }
        }
    }
    int t = Gauss(n);
    if(t == -1) printf("...\n");//无解
    else
    printf("%d\n", t);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值