POJ-3648:Wedding (2-SAT)

题目大意:

有一对新娘新郎要准备婚礼,邀请了n-1对夫妇,有一对长桌,新娘和新郎首先坐在长桌的两侧。

其次对于每对夫妇有如下要求,即丈夫和妻子不能坐在同一侧。

其次其中m对夫妇有通奸关系,有通奸关系的不能同时坐在新娘的对面(可以同性),

求新娘这边一种合理的座位方式。

题目解析:

典型2-SAT问题,从每对夫妇中选择一个人坐在其中一侧。其中有一些约束条件,

但是这题有一个坑点就是新娘也可能有奸情。所以我们选择新郎一侧的人,输出的时候换一侧输出即可。

Ac代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<stack>
#include<queue>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn=2e5+5;
const int INF=1e9+7;

int n,m;
vector<int> v[maxn],r[maxn];
vector< pair<int,char> > ans;

int tot,cnt,low[maxn],dfn[maxn],belong[maxn];
bool vis[maxn];
stack<int> s;
void tarjan(int u)
{
    low[u]=dfn[u]=++tot;
    s.push(u),vis[u]=1;
    for(int i=0;i<v[u].size();i++)
    {
        int now=v[u][i];
        if(!dfn[now])
        {
            tarjan(now);
            low[u]=min(low[u],low[now]);
        }
        else if(vis[now])
            low[u]=min(low[u],dfn[now]);
    }
    if(dfn[u]==low[u])
    {
        cnt++;
        while(!s.empty())
        {
            int now=s.top();
            vis[now]=0,s.pop();
            belong[now]=cnt;
            if(now==u) break;
        }
    }
}
void rebuild()
{
    for(int i=1;i<=2*n;i++)
    {
        for(int j=0;j<v[i].size();j++)
        {
            int now=v[i][j];
            if(belong[i]==belong[now]) continue;
            r[belong[now]].push_back(belong[i]);
        }
    }
}

void add(int x,int y) {  v[x].push_back(y); }

int opp[maxn],sl[maxn],color[maxn];
void dfs(int u)
{
    for(int i=0;i<r[u].size();i++)
    {
        int now=r[u][i];
        if(color[now]==-1)
        {
            color[now]=0;
            dfs(now);
        }
    }
}
void toposort()
{
    for(int i=0;i<=cnt;i++) color[i]=-1;
    for(int i=1;i<=cnt;i++)
        for(int j=0;j<r[i].size();j++)
            sl[r[i][j]]++;
    queue<int> que;
    for(int i=1;i<=cnt;i++)
        if(sl[i]==0) que.push(i);
    while(!que.empty())
    {
        int now=que.front();
        que.pop();
        if(color[now]==-1)
        {
            color[now]=1;
            color[opp[now]]=0;
            dfs(opp[now]);
        }
        for(int i=0;i<r[now].size();i++)
        {
            sl[r[now][i]]--;
            if(sl[r[now][i]]==0)
                que.push(r[now][i]);
        }
    }
}
void SAT()
{
    int flag=0;
    for(int i=1;i<=n;i++)   //判断是否合法
    {
        if(belong[i]==belong[i+n]) flag=1;
        opp[belong[i]]=belong[i+n];
        opp[belong[i+n]]=belong[i];
    }
    if(flag)
    {
        printf("bad luck\n");
        return ;
    }
    toposort(); //拓扑排序
    for(int i=1;i<=n;i++)
    {
        if(color[belong[i]]==1) ans.push_back({i,'w'}); //选择相反的一侧输出
        else ans.push_back({i,'h'});
    }
    sort(ans.begin(),ans.end());    //排序后输出
    for(int i=1;i<ans.size();i++)
        printf("%d%c%c",ans[i].first-1,ans[i].second,i==ans.size()-1?'\n':' ');
}
void init()
{
    ans.clear(),tot=0,cnt=0;
    for(int i=0;i<=2*n;i++) v[i].clear(),r[i].clear();
    for(int i=0;i<=2*n;i++) low[i]=dfn[i]=belong[i]=0;
    for(int i=0;i<=2*n;i++) opp[i]=0,sl[i]=0,vis[i]=0;
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        if(n==0&&m==0) break;
        init();
        for(int i=1;i<=m;i++)
        {
            int x,y;    //w用x+n表示 h用x表示 
            char x1,y1;
            scanf("%d%c%d%c",&x,&x1,&y,&y1);
            x++,y++;
            if(x1=='w'&&y1=='w')    //两人不能坐在同一侧
                add(x+n,y),add(y+n,x);
            if(x1=='w'&&y1=='h')
                add(x+n,y+n),add(y,x);
            if(x1=='h'&&y1=='w')
                add(x,y),add(y+n,x+n);
            if(x1=='h'&&y1=='h')
                add(x,y+n),add(y,x+n);
        }
        v[1+n].push_back(1);//增加一条新娘到新郎的边 表示最后标记一定是新郎一侧的
        for(int i=1;i<=n;i++)   //缩点
        if(!dfn[i]) tarjan(i);
        rebuild();
        SAT();
    //system("pause");
    }
}
/*

*/

猜你喜欢

转载自blog.csdn.net/f2935552941/article/details/81090057
今日推荐