zoj 3656 2-sat 不错的题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了zoj 3656 2-sat 不错的题相关的知识,希望对你有一定的参考价值。

http://acm.zju.edu.cn/onlinejudge/showProblem.do?

problemId=4879


TLE了一下午。然后没办法了 去搜题解 发现思路跟我的差点儿相同 可是就是我的T  后来扩大了数组 然后AC,无语啊

按我的估算 500个点 开到1000+就够了  可是不够 奇怪不懂......

YES or NO的题,一般就是并查集跟2-sat了 目測这道题并查集写起来更easy

http://blog.csdn.net/u011026968/article/details/10823853

看着道题  poj 3678  看完就能发现,仅仅要将每一位都当做poj3678 执行32次 然后就OK

建图方法跟poj3678一样

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <stack>
#include <iostream>
using namespace std;


#define CL(a,b) memset(a,b,sizeof(a))
#define IN(s) freopen(s,"r",stdin)

const int MAXN = 6000;
int n,b[505][505];
int head[MAXN],dfn[MAXN],low[MAXN],id[MAXN];
int cnt,scnt;
stack<int>st;
struct Node{
    int to,nxt;
}edge[1000010];///


inline void addedge(int u,int v,int k)
{
    edge[k].to=v;
    edge[k].nxt=head[u];
    head[u]=k;
    //printf("u=%d v=%d k=%d\n",u,v,k);
}
void tarjan(int u)
{
    int v,i,min1=dfn[u]=low[u]=cnt++;
    st.push(u);
    for(i=head[u];i!=-1;i=edge[i].nxt)
    {
        v=edge[i].to;
        if(dfn[v]==-1)tarjan(v);
        min1=min(min1,low[v]);
    }
    if(min1<low[u]){low[u]=min1;return;}
    do
    {
        v=st.top();
        id[v]=scnt;
        st.pop();
        low[v]=n*2;
    }while(v!=u);
    scnt++;
}

int solve(int pos)
{
    CL(dfn,0xff);
    CL(id,0xff);
    CL(head,0xff);
    CL(low,0xff);

    scnt=cnt=0;
    while(!st.empty())st.pop();
    int num=0;
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
        {
            if(i==j)continue;///
            int f= (b[i][j]>>pos)&1;
            //////////
            //printf("pos=%d bij=%d f=%d\n",pos,b[i][j],f);
            if(i%2 && j%2)
            {
                if(f)
                {
                    addedge(i,j+n,num++);
                    addedge(j,i+n,num++);

                }
                else
                {

                    addedge(i+n,i,num++);
                    addedge(j+n,j,num++);
                    addedge(i,j,num++);
                    addedge(j,i,num++);
                }
                continue;
            }
            if(i%2==0 && j%2==0)
            {
                if(f)
                {

                    addedge(i,i+n,num++);
                    addedge(j,j+n,num++);
                    addedge(i+n,j+n,num++);
                    addedge(j+n,i+n,num++);
                }
                else
                {

                    addedge(j+n,i,num++);
                    addedge(i+n,j,num++);
                }
                continue;
            }
            if(f)
            {
                addedge(i,j+n,num++);
                addedge(j+n,i,num++);
                addedge(j,i+n,num++);
                addedge(i+n,j,num++);
            }
            else
            {
                addedge(i,j,num++);
                addedge(j,i,num++);
                addedge(i+n,j+n,num++);
                addedge(j+n,i+n,num++);
            }
        }
    int flag=1;
    for(int i=0;i<n*2;i++)
        if(dfn[i] == -1)
        {
            ////////
            //printf("tari=%d\n",i);
            ////////
            tarjan(i);
        }

    for(int i=0;i<n;i++)
        if(id[i] == id[i+n])//在同一个连通分量
        {
            flag=0;
            break;
        }
    if(flag)return 1;
    else return 0;
}

int main()
{
    //IN("zoj3656.txt");
    while(~scanf("%d",&n))
    {
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                scanf("%d",&b[i][j]);
        int flag=0;
        for(int i=0;i<n;i++)
        {
            if(b[i][i])
            {
                flag=2;
                puts("NO");
                break;
            }
        }
        if(flag==2)continue;
        for(int i=0;i<n-1;i++)
            for(int j=i+1;j<n;j++)
            {
                if(b[i][j]!=b[j][i])
                {
                    flag=2;
                    puts("NO");
                    break;
                }
            }
        if(flag==2)continue;
        for(int i=0;i<32;i++)
        {
            flag=solve(i);
            if(flag==0)break;
        }
        if(!flag)puts("NO");
        else puts("YES");
    }
    return 0;
}


并查集的做法代码短了非常多 好流弊的样子  http://blog.csdn.net/lasolmi/article/details/38979207


另一种建图方法,网上找的  快了20ms  不是非常理解的说

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <stack>
#include <iostream>
using namespace std;


#define CL(a,b) memset(a,b,sizeof(a))
#define IN(s) freopen(s,"r",stdin)

const int MAXN = 6000;
int n,b[505][505];
int head[MAXN],dfn[MAXN],low[MAXN],id[MAXN];
int cnt,scnt;
stack<int>st;
struct Node{
    int to,nxt;
}edge[1000010];///


inline void addedge(int u,int v,int k)
{
    edge[k].to=v;
    edge[k].nxt=head[u];
    head[u]=k;
    //printf("u=%d v=%d k=%d\n",u,v,k);
}
void tarjan(int u)
{
    int v,i,min1=dfn[u]=low[u]=cnt++;
    st.push(u);
    for(i=head[u];i!=-1;i=edge[i].nxt)
    {
        v=edge[i].to;
        if(dfn[v]==-1)tarjan(v);
        min1=min(min1,low[v]);
    }
    if(min1<low[u]){low[u]=min1;return;}
    do
    {
        v=st.top();
        id[v]=scnt;
        st.pop();
        low[v]=n*2;
    }while(v!=u);
    scnt++;
}

int solve(int pos)
{
    CL(dfn,0xff);
    CL(id,0xff);
    CL(head,0xff);
    CL(low,0xff);

    scnt=cnt=0;
    while(!st.empty())st.pop();
    int num=0;
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
        {
            if(i==j)continue;///
            int f= (b[i][j]>>pos)&1;
            //////////
            //printf("pos=%d bij=%d f=%d\n",pos,b[i][j],f);
            if(i%2 && j%2)
            {
                if(f)
                {
                    //addedge(i,j+n,num++);
                    //addedge(j,i+n,num++);
                    addedge(i+n,j,num++);
                    addedge(j+n,i,num++);
                }
                else
                {
                    addedge(i,i+n,num++);
                    addedge(j,j+n,num++);
                    //addedge(i+n,i,num++);
                    //addedge(j+n,j,num++);
                    //addedge(i,j,num++);
                    //addedge(j,i,num++);
                }
                continue;
            }
            if(i%2==0 && j%2==0)
            {
                if(f)
                {
                    addedge(i+n,i,num++);
                    addedge(j+n,j,num++);

                    //addedge(i,i+n,num++);
                    //addedge(j,j+n,num++);
                    //addedge(i+n,j+n,num++);
                    //addedge(j+n,i+n,num++);
                }
                else
                {
                    addedge(i,j+n,num++);
                    addedge(j,i+n,num++);
                    //addedge(j+n,i,num++);
                    //addedge(i+n,j,num++);
                }
                continue;
            }
            if(f)
            {
                addedge(i,j+n,num++);
                addedge(j+n,i,num++);
                addedge(j,i+n,num++);
                addedge(i+n,j,num++);
            }
            else
            {
                addedge(i,j,num++);
                addedge(j,i,num++);
                addedge(i+n,j+n,num++);
                addedge(j+n,i+n,num++);
            }
        }
    int flag=1;
    for(int i=0;i<n*2;i++)
        if(dfn[i] == -1)
        {
            ////////
            //printf("tari=%d\n",i);
            ////////
            tarjan(i);
        }

    for(int i=0;i<n;i++)
        if(id[i] == id[i+n])//在同一个连通分量
        {
            flag=0;
            break;
        }
    if(flag)return 1;
    else return 0;
}

int main()
{
    //IN("zoj3656.txt");
    while(~scanf("%d",&n))
    {
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                scanf("%d",&b[i][j]);
        int flag=0;
        for(int i=0;i<n;i++)
        {
            if(b[i][i])
            {
                flag=2;
                puts("NO");
                break;
            }
        }
        if(flag==2)continue;
        for(int i=0;i<n-1;i++)
            for(int j=i+1;j<n;j++)
            {
                if(b[i][j]!=b[j][i])
                {
                    flag=2;
                    puts("NO");
                    break;
                }
            }
        if(flag==2)continue;
        for(int i=0;i<32;i++)
        {
            flag=solve(i);
            if(flag==0)break;
        }
        if(!flag)puts("NO");
        else puts("YES");
    }
    return 0;
}



以上是关于zoj 3656 2-sat 不错的题的主要内容,如果未能解决你的问题,请参考以下文章

2-SAT

[poj] 3678 Katu Puzzle

hdu-1814(2-sat)

对于2-sat问题的求解

BZOJ1823 [JSOI2010]满汉全席 2-sat

七月份前计划清单