UVA1391 Astronauts(ACM - ICPC 2006 Europe - Central)(2 - SAT问题、输出方案)

在这里插入图片描述
虽然题目里有A、B、C三种状态,但是每个人只有两个状态可以选择,显然是一道2-SAT; 建图的话,假设选择A(或者B)为i+n,选择C为i;首先当两个人讨厌时,一个人选C,则另一个一定选另一个,连两条边(x,y+n)和(y,x+n),同时若是两个人同类,则还需加上限制使两个人不能同时选A(或同时选B),就再连两条边(x+n,y),(y+n,x);最后跑一边tarjan
输出答案的话就不需要在跑一边拓扑排序了,因为在tarjan时强连通分量被染色的顺序就是拓扑序,所以若col(i)<col(i+n),就表示选择A(或B),否则选C。

注意一个坑,2SAT问题中如果要求你输出方案,如果你的代码输出的跟样例不一样,不要着急,因为2SAT 问题本来就是有多解,结果我样例不过,交上去就A了

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>

using namespace std;
typedef long long ll;
const int N = 5000007, M = 5000007, INF = 0x3f3f3f3f;

int n, m;
int dfn[N], low[N], num;
int head[N], ver[M], nex[M], tot;
int stk[N], top;
bool ins[M];
int scc_cnt;
int color[N];

void init()
{
    
    
    memset(head, -1, sizeof head);
    memset(dfn, 0, sizeof dfn);
    memset(low, 0, sizeof low);
    memset(color, 0, sizeof color);
    tot = num = scc_cnt = 0;
}

int age[N];

void add(int x, int y)
{
    
    
    ver[tot] = y;
    nex[tot] = head[x];
    head[x] = tot ++ ;
}

void tarjan(int x)
{
    
    
    dfn[x] = low[x] = ++num;
    stk[++ top] = x;
    ins[x] = 1;
    for(int i = head[x]; ~i; i = nex[i]){
    
    
        int y = ver[i];
        if(!dfn[y]){
    
    
            tarjan(y);
            low[x] = min(low[x], low[y]);
        }
        else if(ins[y])
            low[x] = min(low[x], dfn[y]);
    }

    if(low[x] == dfn[x]){
    
    
        int y;
        ++scc_cnt;
        color[x] = scc_cnt;
        do{
    
    
            y = stk[top -- ];
            ins[y] = false;
            color[y] = scc_cnt;
        }while(x != y);
    }
}
int main()
{
    
    
    while(scanf("%d%d", &n, &m)!= EOF && n + m){
    
    
        init();
        int sum = 0;
        for(int i = 1;i <= n; ++ i){
    
    
            scanf("%d", &age[i]);
            sum += age[i];
        }
        for(int i = 1; i <= n; ++ i){
    
    
            age[i] = (age[i] * n >= sum);
        }
        for(int i = 1; i <= m; ++ i){
    
    
            int p, q;
            scanf("%d%d", &p, &q);
            //i + n : A / B
            //i : B / C
            if(age[p] == age[q])add(p + n, q), add(q + n, p);
            add(p, q + n),add(q, p + n);
        }

        for(int i =  1; i <= 2 * n; ++i){
    
    
            if(!dfn[i])
                tarjan(i);
        }
        bool flag = 1;
        for(int i = 1; i <= n; ++ i){
    
    
            if(color[i] == color[i + n]){
    
    
                puts("No solution."), flag = 0;
                break;
            }
        }

        if(!flag)continue;
        for(int i = 1; i <= n; ++ i){
    
    
            //谁小选谁 i + n 小的话,就是 i + n : A / B
            if(color[i] > color[i + n]){
    
    
                if(age[i])//根据题意1为大于平均值x,选A,B中的A
                    puts("A");
                else puts("B");
            }
            else puts("C");
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_45697774/article/details/108720193