hdu 4421 Bit Magic【2--Sat----------Tarjan强连通】

来源:互联网 发布:JS怎么将对象转化数组 编辑:程序博客网 时间:2024/05/07 10:13

Bit Magic

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3101    Accepted Submission(s): 875

Problem Description

Yesterday, my teacher taught me about bit operators: and (&), or (|), xor (^). I generated a number table a[N], and wrote a program to calculate the matrix table b[N][N] using three kinds of bit operator. I thought my achievement would get teacher's attention.
The key function is the code showed below.


There is no doubt that my teacher raised lots of interests in my work and was surprised to my talented programming skills. After deeply thinking, he came up with another problem: if we have the matrix table b[N][N] at first, can you check whether corresponding number table a[N] exists?

Input

There are multiple test cases.
For each test case, the first line contains an integer N, indicating the size of the matrix. (1 <= N <= 500).
The next N lines, each line contains N integers, the jth integer in ith line indicating the element b[i][j] of matrix. (0 <= b[i][j] <= 2 31 - 1)

Output

For each test case, output "YES" if corresponding number table a[N] exists; otherwise output "NO".

Sample Input

2

0 4

4 0

3

0 1 24

1 0 86

24 86 0

Sample Output

YES

NO

Source

2012 Asia ChangChun Regional Contest

 

 题目大意:

给你一个这样的矩阵b,是否能有对应的a【】,如果存在,输出YES,如果不存在输出NO。


思路:


1、首先恭喜自己慢慢在学会独立思考问题和解题的能力。其次恭喜耗用两天时间解决这个题,虽然最终Ac的还是擦边球。



2、初解这个题的时候,真的是一时摸不到头脑,2-sat的题目无非两个步骤:

①找到n个党派,每个党派两个人,对应的隐式条件。

②找到那些个矛盾关系,对应的矛盾边。

那么我们就按照这个思路慢慢走下去。


3、问题是是否存在对应的a【】,那么我们将党派的归属划分给a【】,但是对应a【】其中的值,明显不止两种二选一的情况,那么我们继续分析,如何能够将问题转化到让a【】对应的某一因素有二选一的情况。一点一点思考下去,题目给出的是二进制算式,那么我们也考虑a【】=x作为x的二进制有什么作用,那么就不难继续分析,我们可以将每个x的二进制位数上的值作为a和!a。


4、这样我们就找到了a和!a,也就是对应的隐式条件,对应n个党派,每个党派两个人,我们这里规定a为二进制数对应的0值,!a为二进制对应的1值。


5、找到了n个党派,那么我们来继续分析矛盾边。对应b【i】【j】=x,x也可以用一个二进制来表示出来,那么对应的每一位,都是一个二进制计算的结果,这个时候x的每一位就是两个党派之间矛盾关系的存在点:

①与运算&:同1为1,其余为0

②或运算|:有1为1,其余为0

③亦或运算^:不同为1,相同为0

对于每两个点之间有一个操作,和一个操作结果值。我们对矛盾情况进行枚举,我们拿与运算来举个例子:

①c==1:对应矛盾情况就是不让c=1,那么就是让c=0,对于a--->b 其中矛盾边为:a---->!b !a------->b !a-------->!b

②c==0:对应矛盾情况就是不让c=0,那么就是让c=0,对于a----->b 其中矛盾边为:a------->b


同理递推出或运算,亦或运算的建边方式,就能得到一个有向图。


这个时候我欢天喜地的将一个点拆分成62个点,最大图拆分成500*62个点,一个大图跑Tarjan,然后理所应当的被卡了。这是个好事,说明后台数据很多...................


6、然后考虑如何投机取巧搞定卡整个图的方式。继续分析下去:对应每一位的点之间的关系都互相独立,也就是说,对于每一位,我们看成一个图,将整个图拆成31个图,我们跑31遍,如果有NO的情况我们就提前跳出,那么我们就节省了很多建边建点的操作。


7、最终敲定了算法实现,写了一发,提交,很挫的返回了一个1903ms...............


Ac代码:


#include<stdio.h>#include<string.h>#include<algorithm>using namespace std;using namespace std;int head[100000];struct EdgeNode{    int to;    int w;    int next;}e[1000000];int n,cont,sig,cnt,tt;int a[545][545];int stack[3500];int color[3500];int vis[3500];int low[3500];int dfn[3500];void add(int from,int to){    e[cont].to=to;    e[cont].next=head[from];    head[from]=cont++;}int Scan()  {      int res = 0, ch, flag = 0;        if((ch = getchar()) == '-')             //判断正负          flag = 1;        else if(ch >= '0' && ch <= '9')           //得到完整的数          res = ch - '0';      while((ch = getchar()) >= '0' && ch <= '9' )          res = res * 10 + ch - '0';        return flag ? -res : res;  } void Tarjan(int u){    vis[u]=1;    low[u]=dfn[u]=cnt++;    stack[++tt]=u;    for(int i=head[u];i!=-1;i=e[i].next)    {        int v=e[i].to;        if(vis[v]==0)Tarjan(v);        if(vis[v]==1)low[u]=min(low[u],low[v]);    }    if(dfn[u]==low[u])    {        sig++;        do        {            color[stack[tt]]=sig;            vis[stack[tt]]=-1;        }        while(stack[tt--]!=u);    }}int Slove(){    cnt=1;    sig=0;    tt=-1;    memset(dfn,0,sizeof(dfn));    memset(color,0,sizeof(color));    memset(vis,0,sizeof(vis));    memset(stack,0,sizeof(stack));    memset(low,0,sizeof(low));    for(int i=0;i<2*n;i++)    {        if(vis[i]==0)Tarjan(i);    }    for(int i=0;i<n;i++)    {        if(color[i]==color[i+n])return 0;    }    return 1;}int main(){    while(~scanf("%d",&n))    {        for(int i=0;i<n;i++)        {            for(int j=0;j<n;j++)            {                scanf("%d",&a[i][j]);            }        }        int pos;        for(pos=0;pos<31;pos++)        {            cont=0;            memset(head,-1,sizeof(head));            for(int i=0;i<n;i++)            {                for(int j=0;j<n;j++)                {                    if(i==j)continue;                    int u=i;                    int v=j;                    int uu=i+n;                    int vv=j+n;                    int tmp=(a[i][j]&(1<<pos));                    if(i%2==1&&j%2==1)//|                    {                        if(tmp==0)                        {                            add(u,v);                            add(vv,uu);//0 1                            add(uu,vv);                            add(v,u);//1 0                            add(uu,v);                            add(vv,u);//1 1                        }                        else                        {                            add(u,vv);                            add(v,uu);//0 0                        }                    }                    else if(i%2==0&&j%2==0)//&                    {                        if(tmp==0)                        {                            add(uu,v);                            add(vv,u);//1 1                        }                        else                        {                            add(u,vv);                            add(v,uu);//0 0                            add(u,v);                            add(vv,uu);//0 1                            add(uu,vv);                            add(v,u);//1 0                        }                    }                    else // ^                    {                        if(tmp==0)                        {                            add(u,v);                            add(vv,uu);//0 1                            add(uu,vv);                            add(v,u);//1 0                        }                        else                        {                            add(u,vv);                            add(v,uu);//0 0                            add(uu,v);                            add(vv,u);//1 1                        }                    }                }            }            if(Slove()==0)break;        }        if(pos!=31)printf("NO\n");        else printf("YES\n");    }}




0 0
原创粉丝点击