【NOI2017】游戏 题解(2-SAT+缩点)

题目链接

题目大意:有四种场地$a,b,c,x$和三种赛车$A,B,C$,$a$不能跑$A$,$b$不能跑$B$,$c$不能跑$C$,$x$都可以跑。给定$n$个场地和$m$个四元组$(i,h_i,j,h_j)$,意为如果在第$i$个场地跑$h_i$,那么第$j$个场地必须跑$h_j$。输出一种合法方案。若无输出$-1$。

----------------------------------

此题难在建图。把建图搞定就是一个裸的2-SAT板子。

我们规定$0$为每种场地的第一种能跑的车(按字典序),$1$表示第二种能跑的车。对于约束条件,我们有$3$种情况:

1.$h_i$类型的车与$i$冲突,那么我们直接跳过,不用管它。

2.$h_j$类型的车与$j$冲突,那么我们让$i$向$i'$连边,表示如果选择$h_i$则无解。

3.没有冲突情况。那么我们直接按照2-SAT建图方式建就好,即$i$向$j$连边,$j'$向$i'$连边。

对于$x$,我们只要把它当作某一种特定的场地,然后$dfs$枚举所有情况就好。把它当作任意两种场地就可以包含所有可能情况。

时间复杂度$O(2^dn)$。

PS:一开始我超级傻逼,写了一串$if$来判各种情况还各种出锅;后面突然想到只要写个函数判断就行了草。从早上7点来学校写,一直到上午10点才过……

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=200005;
int dfn[maxn],low[maxn],vis[maxn],pos[maxn],jishu,tot;
stack<int> st;
int head[maxn],cnt,n,m,d;
int query[maxn][3];
char ch[maxn][3],c[maxn],s[maxn];
struct node
{
    int next,to;
}edge[maxn];
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline void add(int from,int to)
{
    edge[++cnt].next=head[from];
    edge[cnt].to=to;
    head[from]=cnt;
}
inline void clear()
{
    jishu=tot=cnt=0;
    memset(head,0,sizeof(head));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(pos,0,sizeof(pos));
    while(!st.empty()) st.pop();
}
inline void tarjan(int now)
{
    dfn[now]=low[now]=++jishu;
    st.push(now);vis[now]=1;
    for (int i=head[now];i;i=edge[i].next)
    {
        int to=edge[i].to;
        if (!dfn[to]) tarjan(to),low[now]=min(low[now],low[to]);
        else if (vis[to]) low[now]=min(low[now],dfn[to]);
    }
    if (low[now]==dfn[now])
    {
        tot++;
        while(st.top()!=now)
        {
            int x=st.top();st.pop();
            vis[x]=0;
            pos[x]=tot;
        }
        int x=st.top();st.pop();
        vis[x]=0;
        pos[x]=tot;
    }
}
inline bool check(int pos,char x)
{
    if (x+32==s[pos]) return 1;
    return 0;
}
inline bool get(int pos,char x)
{
    if (s[pos]=='a') return x=='B'?0:1; 
    else return x=='A'?0:1;
}
inline void dfs(int now)
{
    if (now>n)
    {
        clear();
        for (int i=1;i<=m;i++)
        {
            int a=query[i][1],b=query[i][2];
            if (check(a,ch[i][1])) continue;
            if (check(b,ch[i][2]))
            {
                int flag=get(a,ch[i][1]);
                if (!flag) add(a,a+n);
                else add(a+n,a);
                continue;
            }
            else
            {
                int x=get(a,ch[i][1]),y=get(b,ch[i][2]);
                if (x==0&&y==0) add(a,b),add(b+n,a+n);
                if (x==0&&y==1) add(a,b+n),add(b,a+n);
                if (x==1&&y==0) add(a+n,b),add(b+n,a);
                if (x==1&&y==1) add(a+n,b+n),add(b,a);
            }
        }
        for (int i=1;i<=2*n;i++) if(!dfn[i]) tarjan(i);
        for (int i=1;i<=n;i++) if (pos[i]==pos[i+n]) return;
        for (int i=1;i<=n;i++)
        {
            if (pos[i]<pos[i+n])
            {
                if (s[i]=='a') printf("B");
                else printf("A");
            }
            else
            {
                if (s[i]=='c') printf("B");
                else printf("C");
            }
        }
        exit(0);
    }
    if (c[now]=='a') s[now]='a',dfs(now+1);
    if (c[now]=='b') s[now]='b',dfs(now+1);
    if (c[now]=='c') s[now]='c',dfs(now+1);
    if (c[now]=='x'){s[now]='a',dfs(now+1);s[now]='b',dfs(now+1);}
}
int main()
{
    n=read(),d=read();
    cin>>(c+1);
    m=read();
    for (int i=1;i<=m;i++)
    {
        query[i][1]=read(),cin>>ch[i][1];
        query[i][2]=read(),cin>>ch[i][2];
    }
    dfs(1);
    printf("-1");
    return 0;
}
原文地址:https://www.cnblogs.com/Invictus-Ocean/p/13405647.html