码迷,mamicode.com
首页 > 其他好文 > 详细

POJ 3905 Perfect Election (初学2-Sat)

时间:2015-04-24 09:09:08      阅读:233      评论:0      收藏:0      [点我收藏+]

标签:


这篇从原理上理解2-sat如何转化成图论问题简述了如何了实现算法:http://wenku.baidu.com/view/31fd7200bed5b9f3f90f1ce2.html

总的来说2-sat有两种算法,一种用dfs染色搜索出一种解,一种用tarjan(判定是否有解)+拓扑排序构造出任意一个可行解。

dfs从理论上复杂度很高,但是实际上远远达不到上界,而且可以按字典序搜索,实现也简单多了


大致题意:

有n个候选人,m组要求,每组要求关系到候选人中的两个人,“+i +j”代表i和j中至少有一人被选中,“-i -j”代表i和j中至少有一人不被选中。“+i -j”代表i被选中和j不被选中这两个事件至少发生一个,“-i +j”代表i不被选中和j被选中这两个事件至少发生一个。问是否存在符合所有m项要求的方案存在。


思路:每个人是两种情况,选中和不选中,根据每种关系都可以建有向边,果果的2-sat



dfs判解:

//12384K	1704MS

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


const int N = 2020;
vector<int>g[N];
int n,m;
bool mark[N];
int sta[N],top;

inline void add_edge(int u,int c1,int v,int c2)
{
    u=2*(u-1)+c1,v=2*(v-1)+c2;
    g[u^1].push_back(v);
    g[v^1].push_back(u);
}
bool dfs(int u)
{
    if(mark[u^1]) return false;
    if(mark[u]) return true;
    mark[u]=1;
    sta[++top]=u;
    int sz=g[u].size();
    for(int i=0;i<sz;i++)
    {
        int v=g[u][i];
        if(dfs(v)==false) return false;
    }
    return true;
}
bool judge()
{
    for(int i=0;i<n;i++)
    {
        if(!mark[i]&&!mark[i^1])
        {
            top=0;
            if(dfs(i)==false)
            {
                for(int j=1;j<=top;j++)
                    mark[sta[j]]=0;
                top=0;
                if(dfs(i^1)==false) return false;
            }
        }
    }
    return true;
}
void ini()
{
    for(int i=0;i<n;i++) g[i].clear();
    memset(mark,0,sizeof(mark));
    top=0;
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        n*=2;
        ini();
        for(int i=1;i<=m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            if(u>0&&v>0) add_edge(u,1,v,1);
            if(u<0&&v>0) add_edge(-u,0,v,1);
            if(u<0&&v<0) add_edge(-u,0,-v,0);
            if(u>0&&v<0) add_edge(u,1,-v,0);
        }
        if(judge()) puts("1");
        else puts("0");
    }
    return 0;
}

tarjan判解:

//12436K	1672MS	C++	1673B
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;


const int N = 2020;
vector<int>g[N];
int dfn[N],low[N];
int sta[N],top;
int index;
int tmp[N];
int n,m;
inline void add_edge(int u,int c1,int v,int c2)
{
    u=2*(u-1)+c1,v=2*(v-1)+c2;
    g[u^1].push_back(v);
    g[v^1].push_back(u);
}

void tarjan(int u)
{
    dfn[u]=low[u]=++index;
    tmp[u]=1;
    sta[++top]=u;
    int sz=g[u].size();
    for(int i=0;i<sz;i++)
    {
        int v=g[u][i];
        if(tmp[v]==0) tarjan(v);
        if(tmp[v]==1) low[u]=min(low[u],low[v]);
    }
    if(dfn[u]==low[u])
    {
        do
        {
            int v=sta[top];
            tmp[v]=2;
        }while(sta[top--]!=u);
    }
}
void ini()
{
    for(int i=0;i<n;i++) g[i].clear();
    memset(tmp,0,sizeof(tmp));
    index=top=0;
}
int main()
{

    while(~scanf("%d%d",&n,&m))
    {
        n*=2;
        ini();
        for(int i=1;i<=m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            if(u>0&&v>0) add_edge(u,1,v,1);
            if(u<0&&v>0) add_edge(-u,0,v,1);
            if(u<0&&v<0) add_edge(-u,0,-v,0);
            if(u>0&&v<0) add_edge(u,1,-v,0);
        }
        for(int i=0;i<n;i++) if(!tmp[i]) tarjan(i);

        int ans=1;
        for(int i=0;i<n;i++) if(low[i]==low[i^1]) ans=0;
        printf("%d\n",ans);

    }
    return 0;
}

因为dfs判解的时候就已经在过程中把解搜出来了,所以判解还是tarjan的方法简单点效率高点





POJ 3905 Perfect Election (初学2-Sat)

标签:

原文地址:http://blog.csdn.net/kalilili/article/details/45228607

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!