UVA 11294 wedding 2-sat

本文介绍了一种解决特定图论问题的方法,该问题涉及基于约束条件为一组人员分配座位,并通过构造图、进行强连通分量分析及拓扑排序来确保满足所有规则。

可以把一对夫妇当成一个节点,然后拆点的话,h和w分别为真和假,然后直接按照题目中说的建图染色即可

 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
const int maxn =  100;
const int maxm = 1000;
struct node{
    int v,next;
}edge[maxm];
int res[maxm][2],head[maxn],low[maxn],dfn[maxn],stack[maxn],in[maxn],fa[maxn],ha[maxn];
int color[maxn];
int n,m,Time,top,scc_cnt,id;
vector<int>DAG[maxn];
queue<int>que;
void add_edge(int u,int v){
    edge[id].v = v;edge[id].next = head[u]; head[u] = id++;
}
void init(){
    memset(head,-1,sizeof(head));
    memset(in,0,sizeof(in));
    memset(dfn,0,sizeof(dfn));
	memset(fa,0,sizeof(fa));
    id = 0;
	int i;

	int u,v;
    char ch1,ch2;

    for(i = 0; i < m; i++){
        scanf("%d%c %d%c",&u,&ch1,&v,&ch2);
        u <<= 1;v <<= 1;
        if(ch1 == 'w')u ^= 1;
        if(ch2 == 'w')v ^= 1;
        //必须在一起的连边
        add_edge(u^1,v);
        add_edge(v^1,u);
    }
    //这条边必须要加
    add_edge(0,1);
}
int min(int x,int y){
	return x < y ? x : y;
}
void tarjan(int u){//求强连通分量并缩点
    dfn[u] = low[u] = ++Time;
    stack[top++] = u;in[u] = 1;
    for(int id = head[u]; id != -1; id = edge[id].next){
        int v = edge[id].v;
        if(!dfn[v]){
            tarjan(v);
            low[u] = min(low[v],low[u]);
        }
        else if(in[v])low[u] = min(low[u],dfn[v]);
    }
    if( dfn[u] == low[u]){
        scc_cnt++;
        do{
            int v = stack[--top];
            fa[v] = scc_cnt;
            in[v] = 0;
        }while(u != stack[top]);
    }
}
void topsort(){//拓扑排序并着色
	memset(color,0,sizeof(color));
    while(!que.empty()){
        int u = que.front();
        que.pop();
        if(!color[u])color[u] = 1,color[ha[u]] = 2;
        for(int i = 0; i < DAG[u].size(); i++){
            int v = DAG[u][i];
            in[v]--;
            if(!in[v])que.push(v);
        }
    }
}
int main(){
   // freopen("in.txt","r",stdin);
    while(~scanf("%d%d",&n,&m),n||m){
        init();
        Time = top = scc_cnt = 0;
		int i;
        for( i = 0; i < 2*n; i++)
        if(!dfn[i])tarjan(i);
        for(i = 0; i < n; i ++)//夫妻不能做在同一边
        if(fa[i*2] == fa[i*2+1])break;
		else {
			ha[fa[i*2]] = fa[i*2+1];ha[fa[i*2+1]] = fa[i*2];
		}
        if( i < n){
            puts("bad luck");
            continue;
        }
		memset(in,0,sizeof(in));
        for( i = 1; i <= scc_cnt; i++)DAG[i].clear();
        //建反序拓扑图
        for(int u = 0; u < n*2; u++){
            for( id = head[u] ; id != -1; id = edge[id].next){
                int v = edge[id].v;
                if( fa[u] != fa[v]){
                    DAG[fa[v]].push_back(fa[u]);in[fa[u]]++;
                }
            }
        }
		while(!que.empty())que.pop();
		for( i = 1 ; i <= scc_cnt; i++)
			if(!in[i])que.push(i);
		topsort();
		//输出
        for(i = 2; i < 2*n; i += 2){

			if(i!=2)printf(" ");
            if(color[fa[i]] == color[fa[0]] )printf("%dw",i/2);
            else printf("%dh",i/2);
        }
        puts("");
    }
    return 0;
}

  

转载于:https://www.cnblogs.com/LUO257316/p/3234245.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值