【枚举暴力】【UVA11464】 Even Parity

传送门

Description

  给你一个0/1矩阵,可以将矩阵中的0变成1,问最少经过多少此操作使得矩阵任意一元素四周的元素和为偶数。

Input

 第一行是一个整数T代表数据组数,每组数据包含以下内容:

  • 第一行是一个整数n,代表矩阵的行列数
  • 接下来n行每行n个用空格隔开的整数,代表矩阵元素。

Output

 对于每组数据输出一行,格式为Case X: ans

Sample Input

3
3
0 0 0
0 0 0
0 0 0
3
0 0 0
1 0 0
0 0 0
3
1 1 1
1 1 1
0 0 0

Sample Output

Case 1: 0
Case 2: 3
Case 3: -1

Hint

1≤n≤15,数据不超过30组。

Solution

考虑爆搜,显然超时。

考虑如果我们知道了前i行的信息,为了保证第i行是合法的,那么第i+1行放什么元素就被唯一确定了。

换句话说,只要确定了第一行的元素,通过数学归纳法易证,整个矩阵都被唯一确定了。

考虑第一行,只有2n种可能,由于n≤15,完全可以进行枚举。后面依据前面的元素进行判断,复杂度为O(n2),合并复杂度上届为O(2nn2),已经可以通过本题。

通过一些简单的剪纸,程序可以跑的飞快,40ms在lg rk1。

Code

#include<cstdio>
#include<cstring>
#define rg register
#define ci const int

inline void qr(int &x) {
    char ch=getchar(),lst=NULL;
    while(ch>'9'||ch<'0') lst=ch,ch=getchar();
    while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    if (lst=='-') x=-x;
}

char buf[20];
inline void write(int x,const char aft,const bool pt) {
    if(x<0) {putchar('-');x=-x;}
    int top=0;
    do {
        buf[++top]=x%10+'0';
        x/=10;
    } while(x);
    while(top) putchar(buf[top--]);
    if(pt) putchar(aft);
}

template <typename T>
inline T mmax(const T &a,const T &b) {if(a>b) return a;return b;}
template <typename T>
inline T mmin(const T &a,const T &b) {if(a<b) return a;return b;}
template <typename T>
inline T mabs(const T &a) {if(a<0) return -a;return a;}

template <typename T>
inline void mswap(T &a,T &b) {T temp=a;a=b;b=temp;}

const int maxn = 20;
const int INF = 0x3f3f3f3f;

int t,n,cnt,ans;
int MU[maxn][maxn];
int pos[maxn][maxn];

void clear();
void dfs(ci,ci);
void check(ci);

int main() {
    qr(t);
    while(t--) {
        clear();qr(n);
        for(rg int i=1;i<=n;++i) for(int j=1;j<=n;++j) qr(MU[i][j]);
        dfs(1,0);
        if(ans==INF) ans=-1;
        printf("Case %d: %d
",++cnt,ans);
    }
    return 0;
}

void clear() {
    memset(MU,0,sizeof MU);
    memset(pos,0,sizeof pos);
    n=0;ans=INF;
}

void dfs(ci k,ci x) {
    if(x>=ans) return;
    if(k>n) {check(x);return;}
    if(!MU[1][k]) {
        pos[1][k]=0;dfs(k+1,x);
        pos[1][k]=1;dfs(k+1,x+1);
    }
    else {
        pos[1][k]=1;dfs(k+1,x);
    }
}

void check(int x) {
    for(rg int i=2;i<=n;++i) {
        rg int di=i-1;rg int ddi=di-1;
        for(rg int j=1;j<=n;++j) {
            if((pos[di][j-1]+pos[di][j+1]+pos[ddi][j])&1) pos[i][j]=1;else pos[i][j]=0;
            if(pos[i][j]!=MU[i][j]) {
                if(MU[i][j]) return;
                ++x;
                if(x>=ans) return;
            }
        }
    }
    for(rg int i=1;i<=n;++i) {
        if((pos[n][i-1]+pos[n][i+1]+pos[n-1][i])&1) return;
    }
    ans=x;
}

Summary

在答案依附于一个初始状态,且初始状态数可以枚举时,不妨考虑枚举初始状态,凭借此计算出终态。

这样做不仅应用于搜索题,事实上也应用于一部分DP中。

原文地址:https://www.cnblogs.com/yifusuyi/p/9425990.html