zoukankan      html  css  js  c++  java
  • codeforces gym #101987K -TV ShowGame(2-SAT)

    题目链接:

    https://codeforces.com/gym/101987

    题意:

    有长度为$n$的只包含$B,R$的字符串

    有m种关系,每个关系说出三个位置的确切字符

    这三个位置的字符最多有一个是错的

    数据范围:

    $1leq n leq 5000$

    $1leq m leq 10000$

    分析: 

    比如给出1B 2R 3B

    那么如果1是R,2必须是R,3必须是B

    如果2是B,1必须是R,3必须是B

    如果3是R,1必须是B,2必须是R 

    对这些关系建立2—SAT模型

    1B的编号为1,1R的编号为1+n

    缩点,用tarjin算法把两两互达的点连接在一起,这个联通分量要么全被选择要么全部不选

    拓扑排序,从小到大遍历,如果这个点没来过,就标记“选择”,相反点的代表则标记“不选择”,并且传递下去

    如果还是没看懂:https://blog.csdn.net/jarjingx/article/details/8521690

    AC代码:

    #include<bits/stdc++.h>
    #define ll long long
    #define pic pair<int,char>
    using namespace std;
    const int maxn=1e4+7;
    int low[maxn],tim[maxn],tot,vis[maxn],top,boss[maxn],sk[maxn],pick[maxn],n,dp[maxn];
    vector<int>ve[maxn],ve2[maxn];
    void tarjin(int x){
        tim[x]=low[x]=++tot;//加入递归的时间和最低递归时间
        sk[++top]=x;
        vis[x]=1;
        for(int i=0;i<ve[x].size();i++){
            int v=ve[x][i];
            if(tim[v]==0){
                tarjin(v);
                low[x]=min(low[x],low[v]);//对后面产生影响,那么自己也将属于这个联通图
            }else if(vis[v])
                low[x]=min(low[x],low[v]);//构成环了,当然和他联通呀
        }
        if(low[x]==tim[x]){
            int v;
            while(1){
                v=sk[top--];//出栈
                vis[v]=0;
                boss[v]=x;//找到老大了
                if(v==x)break;
            }
        }
    }
    void dfs(int x){//不选择的标记往后传递
        if(pick[x]!=0)return ;
       // cout<<x<<" "<<-1<<endl;
        pick[x]=-1;
        for(int i=0;i<ve2[x].size();i++)
            dfs(ve2[x][i]);
    }
    void tp(){//拓扑排序,这个简单
        queue<int>que;
        for(int i=1;i<=2*n;i++)
            if(boss[i]==i&&dp[i]==0)
                que.push(i);
        while(que.size()){
            int v=que.front();
            if(pick[v]==0){
               // cout<<v<<" "<<1<<endl;
                pick[v]=1;
                int w=v;
                if(w>n)w-=n;
                else w+=n;
                dfs(boss[w]);
            }
            que.pop();
            for(int i=0;i<ve2[v].size();i++){
                dp[ve2[v][i]]--;
                if(dp[ve2[v][i]]==0)que.push(ve2[v][i]);
            }
        }
    }
    int main(){
        int m;
        scanf("%d %d",&n,&m);
        for(int i=1;i<=m;i++){
            pic zz[3];
            for(int j=0;j<3;j++){
                scanf("%d %c",&zz[j].first,&zz[j].second);
                getchar();
            }
            for(int j=0;j<3;j++){
                int v1=zz[j].first,v2=zz[j].first+n;
                if(zz[j].second=='B')
                    swap(v1,v2);
                for(int k=j+1;k<3;k++){
                    int w1=zz[k].first,w2=zz[k].first+n;
                    if(zz[k].second=='R')
                        swap(w1,w2);
                    if(w1!=v1)ve[w1].push_back(v1);//选择了v1就选择w1,这里反向建边
                    if(v2!=w2)ve[v2].push_back(w2);
                }
            }
        }
        for(int i=1;i<=2*n;i++)
            if(tim[i]==0)tarjin(i);
    
        for(int i=1;i<=n;i++)
            if(boss[i]==boss[i+n]){
                printf("-1
    ");
                return 0;
            }
        for(int i=1;i<=2*n;i++){//缩点后重新建边
            for(int j=0;j<ve[i].size();j++){
                int a=boss[i],b=boss[ve[i][j]];
                if(a!=b){
                    ve2[a].push_back(b);
                    dp[b]++;
                }
            }
        }
    
        tp();
        for(int i=1;i<=n;i++){
            if(pick[boss[i]]==-1)
                 printf("R");
            else printf("B");
        }
        printf("
    ");
        return 0;
    }
    

      

  • 相关阅读:
    基于CentOS构建企业镜像站
    Linux命令——set 和 unset
    Bash基础——命令替换
    Bash基础——内置命令
    Linux命令——logger
    Linux命令——pgrep
    Linux下制作静态库 & 动态库
    理解ld-linux.so.2
    Bash基础——printf
    【问题】man手册如何查看区分printf命令和printf函数
  • 原文地址:https://www.cnblogs.com/carcar/p/11572812.html
Copyright © 2011-2022 走看看