HDU 3062 Party 2-SAT(kosaraju)

Party

有n对夫妻被邀请参加一个聚会,因为场地的问题,每对夫妻中只有1人可以列席。在2n 个人中,某些人之间有着很大的矛盾(当然夫妻之间是没有矛盾的),有矛盾的2个人是不会同时出现在聚会上的。有没有可能会有n 个人同时列席?

Input

n: 表示有n对夫妻被邀请 (n<= 1000)

m: 表示有m 对矛盾关系 ( m < (n - 1)2)

在接下来的m行中,每行会有4个数字,分别是 A1,A2,C1,C2

A1,A2分别表示是夫妻的编号

C1,C2 表示是妻子还是丈夫 ,0表示妻子 ,1是丈夫

夫妻编号从 0 到 n -1

Output

如果存在一种情况 则输出YES

否则输出 NO

Sample Input

2

1

0 1 1 1

Sample Output

YES

自己写了一个kosaraju算法解决强连通分量的解法,结果栽在没有考虑多组样例上了(题目也没说有多组样例啊!!可能是其他网站题刷多了,忘了吧)

我理解的2-SAT就是用图模拟逻辑推理的过程,节点代表某种状态,路径(u,v)表有u状态就一定会有v状态。当两种二选一的状态同时出现在一个强连通分量中时,就会出现矛盾,说明不可能。一般题解就是输出“YES”或“NO”。

这里有n对夫妻,用2n个点代表这些人,对应的两个点x1,x2代表妻子去或丈夫去,当x1与y1有矛盾时x1引一条边到y2,说明x1去时一定是y2去(y1一定不会去),同理y1引一条边到x2。

#include<bits/stdc++.h>
using namespace std;

const int MAXN = 1005;
//链式前向星
int G[MAXN<<1];
int G_b[MAXN<<1];   //反向图
int tot;
struct Edge{
    int v,nxt;
}edge[MAXN*MAXN],edge_b[MAXN*MAXN];
void add(int x,int y)
{
    edge[++tot].v=y;
    edge[tot].nxt=G[x];
    G[x]=tot;
    
    edge_b[tot].v=x;
    edge_b[tot].nxt=G_b[y];
    G_b[y]=tot;
} 

int vis[MAXN<<1+5];  //到访记录标记 
int q[MAXN<<1+5];    //新遍历序标记 
int f[MAXN<<1+5];    //存储强连通分量分组归属 
int t;

//强连通 
void dfs1(int x)
{
    vis[x]=1;    //标记此点 
    for(int i=G_b[x];i;i=edge_b[i].nxt) if(!vis[edge_b[i].v]) dfs1(edge_b[i].v);
    q[++t]=x;
} 

void dfs2(int x,int y)
{
    vis[x]=0,f[x]=y;
    for(int i=G[x];i;i=edge[i].nxt) if(vis[edge[i].v]) dfs2(edge[i].v,y);
}

//
int n,m;

int trains(int i)
{
    if(i<=n) 
    return i+n;
    else
    return i-n;
}

void init()
{
    memset(G,0,sizeof(G));
    memset(G_b,0,sizeof(G_b));
    tot=t=0;
    memset(vis,0,sizeof(vis));
}

int main()
{
    int i;
    while(cin>>n>>m)
    {
        init();
            int a1,a2,c1,c2;
            while(m--)
            {
                scanf("%d%d%d%d",&a1,&a2,&c1,&c2);
                a1+=1,a2+=1;         //编号重建    1~n,,,trains  n+1~2n 
                if(c1) a1=trains(a1);
                if(c2) a2=trains(a2);
                
                add(a1,trains(a2));
                add(a2,trains(a1));
            }
            
            for(t=0,i=1;i<=2*n;i++) if(!vis[i]) dfs1(i);
            for(i=2*n;i;i--) if(vis[q[i]]) dfs2(q[i],q[i]);
            
            //从0到 n-1 的 
            bool ans=1;
            for(i=1;i<=n;i++)
            {
                if(f[i]==f[trains(i)]){
                    ans=0;
                    break;
                }
            }
            if(ans){
                cout<<"YES"<<endl;
            }
            else 
            {
                cout<<"NO"<<endl;
            }
    } 
}

拓展:

题目描述

Problem Description

小时候,乡愁是一枚小小的邮票,我在这头,母亲在那头。

—— 余光中

集训是辛苦的,道路是坎坷的,休息还是必须的。经过一段时间的训练,lcy决定让大家回家放松一下,但是训练还是得照常进行,lcy想出了如下回家规定,每一个队(三人一队)或者队长留下或者其余两名队员同时留下;每一对队员,如果队员A留下,则队员B必须回家休息下,或者B留下,A回家。由于今年集训队人数突破往年同期最高记录,管理难度相当大,lcy也不知道自己的决定是否可行,所以这个难题就交给你了,呵呵,好处嘛~,免费**漂流一日。

Input

第一行有两个整数,T和M,1<=T<=1000表示队伍数,1<=M<=5000表示对数。

接下来有T行,每行三个整数,表示一个队的队员编号,第一个队员就是该队队长。

然后有M行,每行两个整数,表示一对队员的编号。

每个队员只属于一个队。队员编号从0开始。

Output

可行输出yes,否则输出no,以EOF为结束。

由于编号是手动输入的,需要重新编号,将队长编号为1~n,两个队员都编号为trains(1~n)

即输入a,b,c 建立新的数组new[MAXN*3] new[a]=i, new[b]=new[c]=trains(i),其他不变

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值