POJ 1681 Painter's Problem 高斯消元+DFS枚举

题意:将下图中的格子由初始状态变为全黄,最少需要多少步。

题解:先确定自由变量,然后枚举自由变量的值,注意剪枝。

#include<cmath>
#include<cstring>
#include<cstdio>
#include<iostream>
using namespace std;

#define MAXN 230
int a[MAXN][MAXN], b[MAXN];
int index[MAXN], flag[MAXN]; //flag[i]==1表示b[i]是自由变量, index[i]记录第i个自由变量的下标
char str[MAXN];
int n, fnum, order; //fnum表示自由变量的数量,order是化简之后的举证的秩
int ans;

int abl ( int x )
{
    return x >= 0 ? x : -x;
}

void Debug ( )
{
    int i , j;
    printf("Debug\n");
    for ( i = 0; i < n; i++ )
    {
        for ( j = 0; j <= n; j++ )
            printf("%d ",a[i][j]);
        printf("\n");
    }
}

/*  x0 x1 x2 x3 x4 x5  b
0    0  1  0  1  1  1  1
1    0  0  0  0  1  0  1
2    0  0  0  0  0  1  0
3    0  0  0  0  0  0  0
4    0  0  0  0  0  0  0
5    0  0  0  0  0  0  0
以此矩阵为例矩阵简单推导一下,便可得下面的求值函数
order = 2, n = 6;
x0, x2, x3 是自由变量;
先求x5: i = order-1 = 2, j = x + 1 = 6
······
*/

int cal ()
{
    int i, j, x, sum = 0;
    x = n - 1;
    for ( i = order - 1; i >= 0; i-- )
    {
        while ( flag[x] && x >= 0 )
            sum += b[x--];

        if ( x >= 0 )
        {
            b[x] = a[i][n];
            for ( j = x + 1; j < n; j++ )
                b[x] ^= ( a[i][j] && b[j] );
            sum += b[x--];
        }
    }
    return sum;
}

void dfs ( int k, int cnt )
{
    if ( cnt >= ans ) //剪枝
        return;
    if ( k >= fnum )
    {
        int tmp = cal();
        if ( ans > tmp ) ans = tmp;
        return;

    }
    b[index[k]] = 0;
    dfs ( k + 1, cnt );
    b[index[k]] = 1;
    dfs ( k + 1, cnt + 1 );
}

int Gauss ()
{
    int i, j, row, col, mr;
    row = col = fnum = 0;
    while ( row < n && col < n )
    {
        mr = row;
        for ( i = row + 1; i < n; i++ )
            if ( abl(a[i][col]) > abl(a[mr][col]) ) mr = i;

        if ( mr != row )
            for ( j = col; j <= n; j++ )
                swap ( a[mr][j], a[row][j] );

        if ( a[row][col] == 0 )
        {
            flag[col] = 1;
            index[fnum++] = col;
            col++; continue;
        }
        for ( i = row + 1; i < n; i++ )
        {
            if ( a[i][col] == 0 ) continue;
            for ( j = col + 1; j <= n; j++ )
                a[i][j] ^= a[row][j];
        }
        row++; col++;
    }
 //Debug();
    order = row;
    for ( i = row; i < n; i++ )
        if ( a[i][n] ) return -1;

    ans = MAXN;
    dfs ( 0, 0 );
    return ans;
}

void init ()
{
    int i, j, k = n * n;
    int t1, t2, t3, t4;
    for ( i = 0; i < k; i++ )
    {
        t1 = i / n;
        t2 = i % n;
        for ( j = 0; j < k; j++ )
        {
            t3 = j / n;
            t4 = j % n;
            if ( abl(t1-t3) + abl(t2-t4) <= 1 )
                a[i][j] = 1;
        }
    }
}

int main()
{
    int t;
    scanf("%d",&t);
    while ( t-- )
    {
        scanf("%d",&n);
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        memset(flag,0,sizeof(flag));
        init();
        int i, j, k, res;
        k = n; n = n * n; i = 0;

        while ( k-- )
        {
            scanf("%s",str);
            for ( j = 0; str[j] != '\0'; j++ )
            {
                if ( str[j] == 'w' ) a[i][n] = 1;
                i++;
            }
        }
        //Debug();
        res = Gauss ();
        if ( res == -1 ) printf("inf\n");
        else printf("%d\n",res);
    }
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值