题目:
题解:
对于读入的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);
}
}