【HNOI2016模拟4.10】线性代数与逻辑

题目:

这里写图片描述

题解:

对于读入的A数组的1部分,反转之后变为0,那我们构建的数组B这个位置必须为1,若这个位置是(i,j),则y[i]^y[j]=1。
我们对y进行染色,如果有冲突,则无解.
否则求出每个联通块的两种颜色的点数。
我们要使!B|A的零的个数最多,则使B的1的个数最多,即y中0,1的个数差距最小。
那么可以直接贪心或者背包求出答案。

Code:

#include <cstdio>
#include <cstring>
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define fd(i, x, y) for(int i = x; i >= y; i --)
using namespace std;

const int N = 1005;
int T, n, a[N][N], bz[N], s[N][2], f[N], g[N], color[N], ans;
int final[N], tot;
struct node {
    int to, next;
}e[N * N * 2];

void link(int x, int y) {
    e[++ tot].next = final[x], e[tot].to = y, final[x] = tot;
    e[++ tot].next = final[y], e[tot].to = x, final[y] = tot;
}

void dg(int x) {
    bz[x] = 1;
    s[x][0] = 1;
    for(int k = final[x]; k; k = e[k].next) {
        int y = e[k].to;
        if(bz[y] && color[y] == color[x]) {
            ans = 0; break;
        }
        if(bz[y]) continue;
        color[y] = !color[x];
        dg(y); if(!ans) break;
        s[x][0] += s[y][1];
        s[x][1] += s[y][0];
    }
}
int main() {
    for(scanf("%d", &T); T; T --) {
        memset(bz, 0, sizeof bz);
        memset(f, 0, sizeof f);
        memset(final, 0, sizeof final);
        memset(e, 0, sizeof e);
        memset(g, 0, sizeof(g));
        memset(s, 0, sizeof(s));
        tot = 0;
        scanf("%d", &n);
        int sum = 0;
        fo(i, 1, n) fo(j, 1, n) scanf("%d", &a[i][j]);
        fo(i, 1, n) fo(j, 1, n) if(a[i][j])
            sum ++, link(i, j);
        ans = 1;
        fo(i, 1, n) if(!bz[i])
            g[i] = 1, dg(i);
        if(!ans) {
            printf("-1\n"); continue;
        }
        f[0] = 1;
        fo(i, 1, n) if(g[i]) {
            fd(j, n, 0) {
                if(j + s[i][0] <= n) f[j + s[i][0]] |= f[j];
                if(j + s[i][1] <= n) f[j + s[i][1]] |= f[j];
            }
        }
        int k = 0;
        fd(i, n / 2, 0) {
            if(f[i]) {
                k = i;
                break;
            }
        }
        printf("%d\n", k * (n - k) * 2 - sum);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值