【UVa1391】宇航员分组Astronauts【2-SAT】【强连通分量】

大意:有n个宇航员,按照年龄划分,年龄低于平均年龄的是年轻宇航员,而年龄大于等于平均年龄的是老练的宇航员。
现在要分配他们去A,B,C三个空间站,其中A站只有老练的宇航员才能去,而B站是只有年轻的才能去,C站都可以去。
有m对宇航员相互讨厌,不能让他们在同一个空间站工作。
输出每个宇航员应分配到哪个空间站,如果没有则输出No solution.

这道题只要构出图来就很好办啦~~
由于每个宇航员只有两种选择,所以我们可以用一个布尔变量 xi 表示第 i 个宇航员的分配方案(true表示A或B,false表示C)。对于属于不同年龄段的互相讨厌的宇航员i j ,只要xi xj 不都为false就行;对于属于同一年龄段的,用一个子句无法表达,需要用两个: xixj xi¯¯¯¯xj¯¯¯¯
然后用 O(m) 的算法搞搞就行了。
顺便把我写的specialJudge放在这里。输入点数和冲突数(边数),以及每个宇航员的分配方案(A,B,C),还有具体冲突情况(边的端点),输出WA或AC。

顺便提一句:我做了一下试验,拓扑排序不逆序会WA。。。还有不必要删除对立节点所属强连通分量的子孙……
具体关于2-SAT的课件在这里(OTZ)

#include <vector>
#include <stack>
#include <cstdio>
#include <iostream>
#include <cstring>
#define CLR(x) memset(x,0,sizeof x)
#define opp(x) ((((x)-1)^1)+1)
#define pb push_back
using namespace std;
const int maxn=100001<<2,maxm=100001<<2;
typedef int arr[maxn];
int next[maxm],to[maxm];
arr list,pre,lowlink,sccno,q,age,color;
bool visit[maxn];
int n,m,dfsClk,tot,sccCnt,tail;
stack<int>s;
vector<int>scc[maxn];
void dfs(int u){
    pre[u]=lowlink[u]=++dfsClk;
    s.push(u);
    for(int k=list[u];k;k=next[k]){
        int v=to[k];
        if(!pre[v]){
            dfs(v);
            lowlink[u]=min(lowlink[u],lowlink[v]);
        }
        else if(!sccno[v])
            lowlink[u]=min(lowlink[u],pre[v]);
    }
    if(lowlink[u]==pre[u]){
        int x;
        ++sccCnt;
        do{
            x=s.top();s.pop();
            sccno[x]=sccCnt;
        }while(x!=u);
    }
}
inline void add(int a,int b){
    tot++;
    next[tot]=list[a];
    list[a]=tot;
    to[tot]=b;
}
inline void add_clause(int x,int xval,int y,int yval){
    x=x+x+xval-1;
    y=y+y+yval-1;
    add(opp(x),y);add(opp(y),x);
}
void topodfs(int u){
    visit[u-2*n]=true;
    for(int k=list[u];k;k=next[k])
        if(!visit[to[k]-2*n]) topodfs(to[k]);
    q[++tail]=u-2*n;
}
inline int read(){
    int x=0;
    char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch))x=x*10+ch-48,ch=getchar();
    return x;
}
inline void init(){
    CLR(list);CLR(pre);CLR(sccno);CLR(color);
    CLR(visit);
    for(int i=0;i<=sccCnt;++i)scc[i].clear();
    dfsClk=sccCnt=tot=tail=0;
    while(!s.empty())s.pop();
}
inline bool work(){
    n=read();m=read();
    if(n==0&&m==0) return false;
    init();
    int a,b;
    double ave=0.0;
    for(int i=1;i<=n;++i) ave+=(age[i]=read());

    ave/=n;
    for(int i=1;i<=m;++i){
        a=read();b=read();
        if((age[a]>=ave)^(age[b]>=ave))
            add_clause(a,1,b,1);
        else add_clause(a,0,b,0),add_clause(a,1,b,1);
    }
    for(int i=1;i<=(n<<1);++i)
        if(!pre[i])dfs(i);
    for(int i=1;i<=n;++i)
        if(sccno[i<<1]&&sccno[(i<<1)-1]&&sccno[i<<1]==sccno[(i<<1)-1])
            {puts("No solution.");return true;}

    for(int i=1;i<=(n<<1);++i){
        for(int k=list[i];k;k=next[k]){
            if(sccno[i]!=sccno[to[k]])
                add(sccno[i]+2*n,sccno[to[k]]+2*n);
        }
    }
    for(int i=2*n+1;i<=sccCnt+2*n;++i)
        if(!visit[i-2*n]) topodfs(i);

    for(int i=1;i<=(n<<1);++i)
        scc[sccno[i]].pb(i);
    CLR(visit);
    for(int i=tail;i;--i){
        int u=q[i],size=scc[u].size();
        if(!visit[u]) for(int j=0;j<size;++j){
            int cur=opp(scc[u][j]);
            color[cur]=1;
            visit[sccno[cur]]=true;
        }
    }
    for(int i=1;i<=n;++i){
        if(!color[i<<1]) puts(age[i]>=ave?"A":"B");
        else puts("C");
    }
    return true;
}
int main(){
    while(work()) ;
}

Special Judge:

#include<bits/stdc++.h>
using namespace std;
set<int>S[3];
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i){
        char ch;
        do{ch=getchar();}while(ch!='A'&&ch!='B'&&ch!='C');
        S[ch-'A'].insert(i);
    }
    for(int i=1;i<=m;++i){
        int a,b;
        scanf("%d%d",&a,&b);
        for(int j=0;j<3;++j){
            if(S[j].count(a)) a=j;
            if(S[j].count(b)) b=j;
        }
        if(a==b) {puts("Wrong Answer!");return 0;}
    }
    puts("Accepted!");
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值