CF1391D 505 题解

CF1391D 505

CF1391D 505

直接粗略看去没有什么可以入手的地方,考虑什么时候是不合法的。

发现样例给出了 7 , 15 7, 15 7,15 的时候是不合法的。

考虑手造几组小样例,发现如果一个大的平方矩阵恰好包含偶数个小的平方矩阵就是不合法的。

考虑 a 2 = k × b 2 a^2 = k\times b^2 a2=k×b2 直接暴力带入一下 b = 2 b = 2 b=2 的情况,发现 a = 4 , k = 4 a = 4, k = 4 a=4,k=4。那么显然对于 n > 3 , m > 3 n > 3, m > 3 n>3,m>3 的情况是不合法的。

那么剩下的状态就很少了,选择行列比较短的一个位置进行状压即可,我们只需要状压当前列的状态然后枚举前面状态进行转移即可。

#include <bits/stdc++.h>
using namespace std;

//#define Fread
//#define Getmod

#ifdef Fread
char buf[1 << 21], *iS, *iT;
#define gc() (iS == iT ? (iT = (iS = buf) + fread (buf, 1, 1 << 21, stdin), (iS == iT ? EOF : *iS ++)) : *iS ++)
#define getchar gc
#endif // Fread

template <typename T>
void r1(T &x) {
	x = 0;
	char c(getchar());
	int f(1);
	for(; c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
	for(; '0' <= c && c <= '9';c = getchar()) x = (x * 10) + (c ^ 48);
	x *= f;
}

template <typename T,typename... Args> inline void r1(T& t, Args&... args) {
    r1(t);  r1(args...);
}

//#define int long long
const int maxn = 2e5 + 5;
const int maxm = maxn << 1;

signed main() {
//    freopen("S.in", "r", stdin);
//    freopen("S.out", "w", stdout);
    int i, j, n, m;
    r1(n, m);
    if(n > 3 && m > 3) return puts("-1"), 0;
    vector<vector<int>> a(n + 2, vector<int>(m + 2, 0));
    for(i = 1; i <= n; ++ i) for(j = 1; j <= m; ++ j) scanf("%1d", &a[i][j]);
    vector<vector<int>> b(min(n, m) + 2, vector<int>(max(n, m) + 2, 0));
    if(n > m)
        for(i = 1; i <= 2; ++ i)
            for(j = 1; j <= m; ++ j)
                b[i][j] = a[j][i];
    if(n > m) swap(n, m);
    const int inf = 0x3f3f3f3f;
    vector<vector<int>> f(m + 2, vector<int>((1 << n) + 5, inf));
    const int z = (1 << n);
    vector<int> ct(z + 5, 0);
    function<int(const int &S, const int &i)> bc;
        bc = [&](const int &S, const int &i) {
            int ct(0);
            for(int j = 1; j <= n; ++ j) {
                int x = (S >> (j - 1)) & 1;
                ct += (x != a[j][i]);
            }
            return ct;
        };
    for(int S = 0; S < z; ++ S) ct[S] = ct[S >> 1] + (i & 1);
    for(int S = 0; S < z; ++ S) f[1][S] = bc(S, 1);
//    printf("%d\n", f[1][3]);
    for(i = 2; i <= m; ++ i) {
        for(int S = 0; S < z; ++ S) {
            auto pd = [&] (const int &a, const int &b) -> bool {
                int ct(0);
                if(n == 1) return true;
                for(int j = 1; j <= 2; ++ j) ct += ((a >> (j - 1)) & 1) + ((b >> (j - 1)) & 1);
                if(!(ct & 1)) return false;
                if(n == 2) return true;
                ct = 0;
                for(int j = 2; j <= 3; ++ j) ct += ((a >> (j - 1)) & 1) + ((b >> (j - 1)) & 1);
                if(!(ct & 1)) return false;
                return true;
            };
            for(int S1 = 0; S1 < z; ++ S1) if(pd(S1, S)) {
//                    puts("YES");
                f[i][S] = min(f[i][S], f[i - 1][S1] + bc(S, i));
            }
//            printf("(%d %d) = %d\n", i, S, f[i][S]);
        }
    }
    int ans(inf);
    for(int S = 0; S < z; ++ S) ans = min(ans, f[m][S]);
    printf("%d\n", ans);
	return 0;
}
/*
2 5
10101
01010
*/
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值