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

15 篇文章 0 订阅
5 篇文章 0 订阅

Description

Description

Input

Input

Output

Output

Sample Input

Sample Input

Sample Output

Sample Output

Data Constraint

Data Constraint

Solution

  • (附:本人第 100 篇博文,为 OI 事业奋斗,多多支持~)。

  • 这道题的题面让人感到云里雾里,但是经过仔细的抽丝剥茧之后就会发现这真“模型”。

  • 首先, X 矩阵是由 Y 向量构造出来的: X[i][j]=y[i] xor y[j]

  • 所以当 X 矩阵的对角线上存在 1 时,就不合法了,输出“-1”(异或运算的性质)。

  • 又由于 X 矩阵的对称性,我们只需处理对角线上方的答案,最后乘2即可。

  • 我们发现,如果每一位 A[i][j]=1 的话,因为 A || !X=!O ,所以要求 X[i][j]=1

  • 所以相当于 y[i]y[j] ,则点 i 向点 j 连一条无向边,表示点 i 和点 j 为对立点。

  • 这样便构出了一张二分图(若有奇环则判为“-1”),用 Friend-Enemy 并查集 维护出联通块。

  • 那么最后答案是所有联通块的 0 的个数乘 1 的个数 。

  • 为了保证答案最优,对于这些连通块,我们可以做一遍类似背包的 O(N2)DP

  • F[i][j] 表示做到第 i 个连通块、0 的个数为 j 1 的最大个数 ,转移显然。

  • 由于 X 取过反 , 1 的最大个数即为所求。

  • 又由于开始的状态有 1 ,会计算重复,我们减去算出的 连边条数(意义相同) 即可。

  • 那么连边、DP复杂度相等,这样总时间复杂度即为 O(N2)

Code

#include<cstdio>
#include<cstring>
#define clr(x,y) memset(x,y,sizeof(x));
using namespace std;
const int N=1001;
int n,tot,num;
int first[N],next[N<<1],en[N<<1];
int f[N][N],g[N],h[N][2],fa[N];
bool judge;
bool a[N][N];
inline int read()
{
    int X=0,w=1; char ch=0;
    while(ch<'0' || ch>'9') {if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar();
    return X*w;
}
inline int max(int x,int y)
{
    return x>y?x:y;
}
inline int get(int x)
{
    if(fa[x]==x) return x;
    return fa[x]=get(fa[x]);
}
inline void insert(int x,int y)
{
    next[++tot]=first[x];
    first[x]=tot;
    en[tot]=y;
}
inline void init_link()
{
    tot=num=0;
    clr(first,0);clr(g,-1);clr(h,0);clr(f,-1);
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=1;i<n;i++)
        for(int j=i+1;j<=n;j++)
            if(a[i][j])
            {
                insert(i,j);
                insert(j,i);
                int f1=get(i),f2=get(j);
                if(f1!=f2) fa[f1]=f2;
            }
}
inline void dfs(int x,int y)
{
    h[num][g[x]]++;
    for(int i=first[x];i;i=next[i])
        if(en[i]!=y)
        {
            if(g[en[i]]==g[x])
            {
                judge=true;
                return;
            }
            if(g[en[i]]!=-1) continue;
            g[en[i]]=g[x]^1;
            dfs(en[i],x);
            if(judge) return;
        }
}
inline void dp()
{
    int ans=f[0][0]=0;
    for(int i=1;i<=num;i++)
        for(int j=0;j<=n;j++)
        {
            int x=h[i][0],y=h[i][1];
            if(j>=x && f[i-1][j-x]!=-1) f[i][j]=max(f[i][j],f[i-1][j-x]+y);
            if(j>=y && f[i-1][j-y]!=-1) f[i][j]=max(f[i][j],f[i-1][j-y]+x);
        }
    for(int j=0;j<=n;j++) ans=max(ans,f[num][j]*j);
    printf("%d\n",(ans<<1)-tot);
}
inline void work()
{
    n=read();
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++) a[i][j]=read();
    for(int i=1;i<=n;i++)
        if(a[i][i])
        {
            printf("-1\n");
            return;
        }
    init_link();
    for(int i=1;i<=n;i++)
        if(get(i)==i)
        {
            judge=g[i]=0,num++;
            dfs(i,0);
            if(judge)
            {
                printf("-1\n");
                return;
            }
        }
    dp();
}
int main()
{
    int T=read();
    while(T--) work();
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值