天天写算法之(2-sat)Peaceful Commission

地址: 点击打开链接
又来了一个新的,脑壳疼,越往后面,一接触新的东西都要大费周章一波。

这个题目,直接看了网上的模板,我把每行都注释了一下。哎,每次都得看别人毫无注释的模板,然后理解,真的费劲-。-|||
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int maxn=8003;
/********************** 2-sat模板 **********************/
struct Twosat{
    int n;//有多少个组别
    vector<int> g[maxn*2];//记录选?必选?或者选?必不选?
    bool mark[maxn*2];//是否被访问过
    int s[maxn*2],c;//s用来记录结果。c用来索引,也就是下标。
    
    bool dfs(int x){
        if(mark[x^1]) return false;//本来应该是选a必选b,结果你dfs过来,发现竟然选的是b^1,因此直接false;
        if(mark[x]) return true;//如果已经访问过了,说明走了一遍了。
        mark[x]=true;//标记,这一步是最开始的时候用的
        s[c++]=x;//记录整个过程,访问过的数据的大小
        for(int i=0;i<(int)g[x].size();i++)
            if(!dfs(g[x][i])) return false;
        return true;
    }
    void init(int n){
        this->n=n;
        for(int i=0;i<n*2;i++) g[i].clear();
        memset(mark,0,sizeof(mark));
    }
    //这里是选x必不能选y,但是内部记录是,选x必选y^1,也就是奇变偶,偶变奇
    void add_clause(int x,int y){//这个函数随题意变化
        g[x].push_back(y^1);//选了x就必须选y^1
        g[y].push_back(x^1);
    }
    bool solve(){
        //搜索所有的点,从不同的点进行dfs查看结果
        for(int i=0;i<n*2;i+=2)
            if(!mark[i]&&!mark[i+1]){
                c=0;
                if(!dfs(i)){//每次dfs都是偶数
                    while(c>0) mark[s[--c]]=false;//取消掉全部mark的东西
                    if(!dfs(i+1)) return false;//如果i+1也行不通,那完了,这个组没有希望了
                }
            }
        return true;
    }
};
/*********************** 2-sat模板 ************************/
int main(){
    int n,m,a,b;
    Twosat solver;
    while(~scanf("%d%d",&n,&m)){
        solver.init(n);
        for(int h=0;h<m;h++){
            scanf("%d%d",&a,&b);//a,b不能同时选
            a--;b--;
            solver.add_clause(a,b);
        }
        if(solver.solve()){
            for(int i=0;i<n*2;i++)
                if(solver.mark[i]) printf("%d\n",i+1);//输出选择的都是谁
        }
        else printf("NIE\n");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_36616268/article/details/80584719
今日推荐