POJ 3648 Wedding(2-SAT 拓扑排序输出任意一种解决方案)

题目链接:http://poj.org/problem?id=3648

Description

Up to thirty couples will attend a wedding feast, at which they will be seated on either side of a long table. The bride and groom sit at one end, opposite each other, and the bride wears an elaborate headdress that keeps her from seeing people on the same
side as her. It is considered bad luck to have a husband and wife seated on the same side of the table. Additionally, there are several pairs of people conducting adulterous relationships (both different-sex and same-sex relationships are possible), and it
is bad luck for the bride to see both members of such a pair. Your job is to arrange people at the table so as to avoid any bad luck.

Input

The input consists of a number of test cases, followed by a line containing 0 0. Each test case gives n, the number of couples, followed by the number of adulterous pairs, followed by the pairs, in the form "4h 2w" (husband from couple 4, wife from
couple 2), or "10w 4w", or "3h 1h". Couples are numbered from 0 to n - 1 with the bride and groom being 0w and 0h.

Output

For each case, output a single line containing a list of the people that should be seated on the same side as the bride. If there are several solutions, any one will do. If there is no solution, output a line containing "bad luck".

Sample Input

10 6
3h 7h
5w 3w
7h 6w
8w 3w
7h 3w
2w 5h
0 0

Sample Output

1h 2h 3w 4h 5h 6h 7h 8h 9h

Source

Waterloo Local Contest, 2007.9.29

题意:

有一对新人结婚,邀请了 n 对夫妇去参加婚礼。

有一张很长的桌子,每个人只能坐在桌子的两边,

还要满足下面的要求:

1.每对夫妇不能坐在同一侧

2.n对夫妇之中可能有通奸关系(包括男男,男女,女女),有通

奸关系的不能同时坐在新娘的对面,可以分开坐,可以

同时坐在新娘这一侧。

如果存在一种可行的方案,输出与新娘同侧的人。

PS:

求解的时候去选择和新郎同一侧的人,输出的时候换一下就是新娘同一侧的人。

如果i和j有奸情,则增加一条i到j‘,j到i‘的边,

同时增加一条新娘到新郎的边,表示必须选新郎。

本题直接选新娘一边的容易错。因为新娘也可能有奸情,需要排除

代码如下:

/*
1.根据偷奸关系建图(1h和2h有偷奸关系,建边1h->2w    2h->1w)
2.求强连通分量
3.判断有无解(任一对夫妇不在同一强连通分量中,有解;否则无解)
4.缩点建图(建反向图)
5.拓扑排序
6.由底向上求解(由于上面建的是反向图,所以自顶(无入度)向下)
至此,选出来的是做新娘对面的,要求输出,坐在新娘一边的
*/
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
//2-SAT 强连通缩点
const int MAXN = 1010;
const int MAXM = 100010;
struct Edge
{
    int to;
    int next;
} edge[MAXM];
int head[MAXN],tot;
void init()
{
    tot = 0;
    memset(head,-1,sizeof(head));
}
void addedge(int u,int v)
{
    edge[tot].to = v;
    edge[tot].next = head[u];
    head[u] = tot++;
}
int Low[MAXN],DFN[MAXN],Stack[MAXN],Belong[MAXN];//Belong数组的值1~scc
int Index,top;
int scc;
bool Instack[MAXN];
int num[MAXN];
void Tarjan(int u)//tarjan求强连通分量
{
    int v;
    Low[u] = DFN[u] = ++Index;
    Stack[top++] = u;
    Instack[u] = true;
    for(int i = head[u]; i != -1; i = edge[i].next)
    {
        v = edge[i].to;
        if( !DFN[v] )
        {
            Tarjan(v);
            if(Low[u] > Low[v])Low[u] = Low[v];
        }
        else if(Instack[v] && Low[u] > DFN[v])
            Low[u] = DFN[v];
    }
    if(Low[u] == DFN[u])
    {
        scc++;
        do
        {
            v = Stack[--top];
            Instack[v] = false;
            Belong[v] = scc;
            num[scc]++;
        }
        while(v != u);
    }
}

//判断是否有解
bool solvable(int n)//n是总个数,需要选择一半
{
    memset(DFN,0,sizeof(DFN));
    memset(Instack,false,sizeof(Instack));
    memset(num,0,sizeof(num));
    Index = scc = top = 0;
    for(int i = 0; i < n; i++)
        if(!DFN[i])
            Tarjan(i);
    for(int i = 0; i < n; i += 2)
    {
        if(Belong[i] == Belong[i^1])
            return false;
    }
    return true;
}

//拓扑排序求任意一组解部分
queue<int>q1,q2;
vector<vector<int> > dag;//缩点后的逆向DAG图
char color[MAXN];//染色,为'R'是选择的
int indeg[MAXN];//入度
int cf[MAXN];
void topsort(int n)
{
    dag.assign(scc+1,vector<int>());
    memset(indeg,0,sizeof(indeg));
    memset(color,0,sizeof(color));
    for(int u = 0; u < n; u++)//构建反向缩点图
        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            int v = edge[i].to;
            if(Belong[u] != Belong[v])
            {
                dag[Belong[v]].push_back(Belong[u]);
                indeg[Belong[u]]++;
            }
        }
    for(int i = 0; i < n; i += 2)
    {
        cf[Belong[i]] = Belong[i^1];
        cf[Belong[i^1]] = Belong[i];
    }
    while(!q1.empty())q1.pop();
    while(!q2.empty())q2.pop();
    for(int i = 1; i <= scc; i++)
        if(indeg[i] == 0)
            q1.push(i);
    while(!q1.empty())
    {
        int u = q1.front();
        q1.pop();
        if(color[u] == 0)
        {
            color[u] = 'R';
            color[cf[u]] = 'B';
        }
        int sz = dag[u].size();
        for(int i = 0; i < sz; i++)
        {
            indeg[dag[u][i]]--;
            if(indeg[dag[u][i]] == 0)
                q1.push(dag[u][i]);
        }
    }
}
int change(char s[])
{
    int ret = 0;
    int i = 0;
    while(s[i]>='0' && s[i]<='9')
    {
        ret *= 10;
        ret += s[i]-'0';
        i++;
    }
    if(s[i] == 'w') //女
        return 2*ret;
    else            //男
        return 2*ret+1;
}
int main()
{
    int n, m;
    char s1[17], s2[17];
    while(~scanf("%d%d",&n,&m))
    {
        if(n == 0 && m == 0)
            break;
        init();
        while(m--)
        {
            scanf("%s%s",s1,s2);
            int u = change(s1);
            int v = change(s2);
            addedge(u^1,v);//注意方向,模拟一下
            addedge(v^1,u);
        }
        addedge(1,0);
        if(solvable(2*n))//判断是否有解
        {
            topsort(2*n);
            for(int i = 1; i < n; i++)
            {
                //注意这一定是判断color[Belong[
                if(color[Belong[2*i]] == 'R')
                    printf("%dw",i);
                else
                    printf("%dh",i);
                if(i < n-1)
                    printf(" ");
                else
                    printf("\n");
            }
        }
        else
            printf("bad luck\n");
    }
    return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-26 18:23:52

POJ 3648 Wedding(2-SAT 拓扑排序输出任意一种解决方案)的相关文章

poj 2367 Genealogical tree【拓扑排序输出可行解】

Genealogical tree Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 3674   Accepted: 2445   Special Judge Description The system of Martians' blood relations is confusing enough. Actually, Martians bud when they want and where they want. T

POJ 1128 Frame Stacking(拓扑排序&amp;#183;打印字典序)

题意  给你一些矩形框堆叠后的鸟瞰图  推断这些矩形框的堆叠顺序  每一个矩形框满足每边都至少有一个点可见  输入保证至少有一个解 按字典序输出全部可行解 和上一题有点像  仅仅是这个要打印全部的可行方案  建图还是类似  由于每一个矩形框的四边都有点可见  所以每一个矩形框的左上角和右下角的坐标是能够确定的  然后一个矩形框上有其他字符时  就让这个矩形框相应的字符和那个其他字符建立一个小于关系  由于要打印方案  所以在有多个入度为0的点时须要用DFS对每种选择都进行一遍拓扑排序 #incl

POJ 3249 Test for Job 拓扑排序+DP

http://poj.org/problem?id=3249 题意: 给一个有向无环图DAG(不一定联通),每个点有权值,入度为0的点为起点,出度为0的点为终点,选择一个起点走到一个终点,使得路上的权和最大. 分析: dp[to] = max(dp[from]) + value[to],然后先拓扑排序保证状态正确转移即可,终点做标记,如果是终点则尝试更新答案. update:因为点权可以为负,所以程序里用dp[i] == -1表示未访问过该点是有问题的,不过没有遇上会卡掉这种情况的数据=.= 1

POJ 3648 Wedding (2-SAT+输出可行解)

题目地址:POJ 3648 这题终于AC了....没有专门对新郎新娘加一条边.. 这题前面一直读错题意了,调试了好长时间样例也没过..这题的意思是只要新郎那一边没有通奸的就可以,然后输出新娘那一边的人. 然后就是对那些有**关系的加边,由于新郎新娘必须要在两侧,所以最后要额外加一条边.然后用强连通判断,逆拓扑染色输出可行解即可. 代码如下: #include <iostream> #include <cstdio> #include <string> #include

POJ 3687-Labeling Balls(逆序拓扑排序)

Labeling Balls Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 11256   Accepted: 3230 Description Windy has N balls of distinct weights from 1 unit to N units. Now he tries to label them with 1 to N in such a way that: No two balls share

拓扑排序输出

拓扑排序如果是随便输出的话,那么只需要用一个队列就可以维护了.复杂度O(n) 如果是要求字典序最小输入的话,使用小堆维护,正常建边即可.复杂度O(nlog(n)) 如果要求尽量使最小值在前输出的话(例如312 优先级高于 231)那么可以反向建边,然后在使用大堆维护,最后把顺序reverse()下.复杂度同上 对于第三个:题目1 题目2 简单证明下:就是假如(u, v)之间是有间接或者直接的拓扑关系的话,那么其实(u,v)之间的关系是有它们的偏序关系决定,如果(u,v)之间没有拓扑关系的话,那么

POJ 3687 Labeling Balls 逆向拓扑排序

链接: poj3687 题意: 有N个标号为1~N的小球,重量(不包括断言)依次增加 ,现给出M句断言 ,断言格式为a b 表示小球a轻于小球b     要求根据重量大小依次输出1~N号小球应在的的位置(重量递增)不满足断言则输出-1 题解: 因为重量是依次增加的  不能按常规的构造edge[a][b]=1生成拓扑排序 既然关系格式和一般拓扑排序是相反的  我们可以尝试着构造反向边edge[b][a]=1: indegree[a]++;   再根据小球标号从后往前进行拓扑排序,即可得到一幅完整的

POJ 1128 Frame Stacking(拓扑排序&#183;打印字典序)

题意  给你一些矩形框堆叠后的俯视图  判断这些矩形框的堆叠顺序  每个矩形框满足每边都至少有一个点可见  输入保证至少有一个解 按字典序输出所有可行解 和上一题有点像  只是这个要打印所有的可行方案  建图还是类似  因为每个矩形框的四边都有点可见  所以每个矩形框的左上角和右下角的坐标是可以确定的  然后一个矩形框上有其它字符时  就让这个矩形框对应的字符和那个其它字符建立一个小于关系  由于要打印方案  所以在有多个入度为0的点时需要用DFS对每种选择都进行一遍拓扑排序 #include

POJ 3687 Labeling Balls【拓扑排序 优先队列】

题意:给出n个人,m个轻重关系,求满足给出的轻重关系的并且满足编号小的尽量在前面的序列 因为输入的是a比b重,但是我们要找的是更轻的,所以需要逆向建图 逆向建图参看的这一篇http://blog.csdn.net/scf0920/article/details/28108243 然后用优先队列来实现的参看的这一篇 http://ycool.com/post/u9ahrwg#algo3 1 #include<iostream> 2 #include<cstdio> 3 #includ