hdu3062 Party tarjan + 2-SAT

题面

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

题解

​ 发发两者不能同时出现,符合2-SAT模型,因为m的数据过大,所以常规的dfs算法的O(n(n+m))的复杂度不能承受。
​ 又其提问为是否存在最大解,故将原问题建图并求强连通分量,根据点与点之间关系可以直接得出答案。

​ 考虑建图,因为一对夫妻之间相互排斥且要求至少有一人入选,所以可以考虑将夫妻作为原建图习惯中的排斥点来建图,即选了A后就不能选A的对象;不选A则一定要选A的对象。

代码

#include
#include
#include
#include
using namespace std;

const int maxn = 2e3+100;
const int maxm = 5e6+10;

int fi[maxn], ecnt;
int dfn[maxn], low[maxn], st[maxn], bel[maxn];
    //bel -> belong;
int n, m, top, bcnt, bnum;
    //bnum -> 时间戳;
bool vis[maxn];

struct Edge {
    int v, next;
}e[maxm];

void add_edge(int u, int v) {
    e[ecnt] = (Edge){v, fi[u]};
    fi[u] = ecnt++;
}

void init() {
    memset(fi, -1, sizeof(fi));
    memset(dfn, 0, sizeof(dfn));
    ecnt = top = bnum = bcnt = 0;
}

void Tarjan(int x) {
    dfn[x] = low[x] = ++bnum;
    vis[x] = true;
    st[++top] = x;
    for(int i = fi[x]; i != -1; i = e[i].next) {
        int v = e[i].v;
        if(!dfn[v]) {
            Tarjan(v);
            low[x] = min(low[x], low[v]);
        } else if(vis[v] && dfn[v] < low[x])
            low[x] = dfn[v];
    }
    if(dfn[x] == low[x]) {
        bel[x] = ++bcnt;
        vis[x] = 0;
        int v = st[top];
        while(v != x) {
            vis[v] = 0;
            bel[v] = bcnt;
            v = st[--top];
        }
        --top;
    }
}

int main() {
    bool flag = 0;
    while(~scanf("%d%d", &n, &m)) {
        flag = 1;   init();
        for(int i = 1; i <= m; i++) {
            int x1, x2, y1, y2;
            scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
            add_edge(x1*2+x2, y1*2+!y2);
            add_edge(y1*2+y2, x1*2+!x2);
        }
        for(int i = 0; i < n*2; i++) if(!dfn[i]) Tarjan(i);
        for(int i = 0; i < n; i++)
            if(bel[i*2] == bel[i*2+1]) {
                printf("NO\n");
                flag = 0;
                break;
            }
        if(flag)    printf("YES\n");
    }
}

你可能感兴趣的:(图论)