题目大意:
有一对新娘新郎要准备婚礼,邀请了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");
}
}
/*
*/