POJ 3648-Wedding(2-SAT)

来源:互联网 发布:陕西大数据集团 编辑:程序博客网 时间:2024/05/17 07:27


【题目大意】很多对夫妇参加一对新人的婚礼。分别做在长桌子的两侧。新郎、新娘分别坐两侧,新娘只能看到她对面的人。新娘不想看到她对面有夫妇。
而且有一些人是有通奸关系的(男的和男的有,女的和男的、女的和女的都可能有,而且新郎也可能和别人有通奸关系),新娘不想看到有通奸关系一对人。
也就是有通奸关系的不能一起坐在新娘对面。
输入是:_n对夫妇(包括新郎新娘在女的,编号为0-(n-1),新郎、新娘那一对的编号为0),_m对通奸关系。

接下来_m行有通奸关系的。h表示男的,w表是女的,3w 5h即表示第三对夫妇的女的和第五对夫妇的男的有不寻常关系


思路:要转化成一个比较好的2-sat, 一开始我想的是,每个人都可以坐在右面也可以坐在左面,这个就是个2-sat, 那么夫妇, 通奸的人都是限制关系。。这样也可以只不过有点麻烦, 这样建图看这个大神的吧 ->点击打开链接

题意:

        有一对新人结婚,n-1对夫妇去参加婚礼.有一个很长的座子,新娘与新郎坐在座子的两边(相反).接下来n-1对夫妇就坐,其中任何一对夫妇都不能坐在同一边,且(有一些人有奸情)这些有奸情的两个人不能同时坐在新娘对面.(只能分开做,或者都坐到新娘一边去)。对于每个输入实例,输出应该坐在新娘同一边的人编号。

分析:

        由于有n对夫妇(0号表示新婚夫妻).所以我们这里用0表示第0对的妻子,1表示第0对的丈夫. 2*i表示第i对的夫人,2*i+1表示第i对的丈夫.一共就有2*n个人了.

        然后对于每个人来说,把他分成两个节点,如果该人在做左边就mark[i*2],如果该人坐右边就mark[i*2+1].

        我令新娘直接坐左边即第0个人mark[0]=true,新郎直接坐右边即第1个人mark[1*2+1]=true.

        然后对于每对夫妻,因为他们不能在同一边,所以第i对夫妻中a= 2*i表示妻子,b=2*i+1表丈夫. 有这样的关系:

a在左边,那么b就在右边,a*2->b*2+1

a在右边,那么b就在左边,a*2+1->b*2

b在左边,那么a就在右边,b*2->a*2+1

b在右边,那么a就在左边,b*2+1->a*2

        然后对于每对有奸情的人a与b,因为它们不能同时在新娘对面(右边),所以:

a*2+1->b*2

b*2+1->a*2

        注意首先我们定了新娘(0号)在左边,新郎(第1号人)一定在右边,所以我们要先加上:

0*2+1->0*2  和 1*2->1*2+1 

这样就保证了新娘和新郎在固定的那边不动.


2-sat问题一定要动脑子。。。想办法转化成2-sat问题, 如果想出很暴力的也要想想,有没有更巧妙的转化? 

先回顾下2-sat的实际应用: 在实际问题中,2-SAT问题在大多数时候表现成以下形式:有N对物品,每对物品中必须选取一个,也只能选取一个,并且它们之间存在某些限制关系(如某两个物品不能都选,某两个物品不能都不选,某两个物品必须且只能选一个,某个物品必选)等,这时,可以将每对物品当成一个布尔值(选取第一个物品相当于0,选取第二个相当于1),如果所有的限制关系最多只对两个物品进行限制,则它们都可以转化成9种基本限制关系,从而转化为2-SAT模型。

也就是限制关系不一定限制到每一个, 但是每组只能出一个是死的。那这题,夫妇之间就是个2-sat关系, 每一对夫妇只能选一个人做到新娘对面,也就是从2n个人选出n个人来, 其中通奸的不能同时出现, 这就变成了和平大使那题啦,如果u和v有通奸关系,就连边u->v',v->u'。有一点需要注意,就是要连一条边0->1这样如果选了0就必须选1,那么就矛盾了,所以0一定不被选,选出来的就是新郎那一边的。很巧妙啊!这样连边也可以这样思考, 他说的是不能同时出现在对面, 也就是可以都不出现在他对面, 这样就是 选了i',j'了,这样两个i,j都去新娘那边了。。 如果通奸的人 必须一人一边的话, 那么就必须是 i-j', j-i',i'-j,j'-i了~ 注意不能同时出现跟一边一人的区别~


#include <iostream>#include <cstring>#include <cstdio>#include <vector>#include <stack>#include <algorithm>using namespace std;typedef long long ll;const int maxn = 2e4 + 5;int n, m, low[maxn], dfn[maxn], id[maxn], scc_cnt, dfs_cnt, cnt[maxn];int S[maxn], T[maxn], D[maxn], in[maxn], col[maxn], opp[maxn];vector<int> v[maxn], vt[maxn];stack<int> s;void init(){    memset(low, 0, sizeof(low));    memset(id, 0, sizeof(id));    memset(dfn, 0, sizeof(dfn));    memset(in, 0, sizeof(in));    memset(col, -1, sizeof(col));    memset(opp, 0, sizeof(opp));    scc_cnt = dfs_cnt = 0;    for(int i = 0; i < maxn; i++)        v[i].clear();    while(!s.empty())        s.pop();}void addedge(int x, int y){    v[x].push_back(y);}void addedge2(int x, int y){    vt[x].push_back(y);}void tarjan(int x){    dfn[x] = low[x] = ++dfs_cnt;    s.push(x);    for(int i = 0; i < v[x].size(); i++)    {        int to = v[x][i];        if(!dfn[to])        {            tarjan(to);            low[x] = min(low[x], low[to]);        }        else if(!id[to])            low[x] = min(low[x], dfn[to]);    }    if(low[x] == dfn[x])    {        scc_cnt++;        while(1)        {            int u = s.top();            s.pop();            id[u] = scc_cnt;            if(x == u) break;        }    }}void scc(){    for(int i = 0; i < 2*n ; i++)        if(!dfn[i])            tarjan(i);}int Q[maxn];void top(){    memset(in, 0, sizeof(in));    memset(col, -1, sizeof(col));    for(int i = 0; i < 2*n; i++) vt[i].clear();    for(int i = 0; i < 2*n; i++)    {        for(int j = 0; j < v[i].size(); j++)        {            int to = v[i][j];            if(id[i] == id[to]) continue; // 在同一个连通分量不用考虑            addedge2(id[to], id[i]); //反着存边            in[id[i]]++; //入度++        }    }    int frnt, rear;    frnt = rear = 0;    for(int i = 1; i <= scc_cnt; i++)        if(!in[i]) Q[rear++] = i;    while(frnt != rear)    {        int u = Q[frnt++];        if(col[u] == -1) //未染色        {            col[u] = 1;  //染色1            col[opp[u]] = 0; //对立点染色0        }        for(int i = 0; i < vt[u].size(); i++)        {            int to = vt[u][i];            if(--in[to] == 0)                Q[rear++] = to;        }    }}int main(){    while(~scanf("%d%d", &n, &m), n+m)    {        init();        int x, y;        char ch1, ch2;        while(m--)        {            scanf("%d%c%d%c", &x, &ch1, &y, &ch2);            int i, j, ii, jj;            if(ch1 == 'h')            {                i = x*2+1;                ii = i-1;            }            else            {                i = x*2;                ii = i+1;            }            if(ch2 == 'h')            {                j = y*2+1;                jj = j-1;            }            else            {                j = y*2;                jj = j+1;            }            addedge(i, jj); //i-j'            addedge(j, ii); // j - i'        }        addedge(0, 1);        scc();        int flag = 0;        for(int i = 0; i < 2*n; i+=2)        {            if(id[i] == id[i+1])            {                flag = 1;                break;            }            opp[id[i]] = id[i+1];            opp[id[i+1]] = id[i];        }        if(flag)        {            printf("bad luck\n");            continue;        }        top();        int goal = col[id[0]];        for(int i = 2; i < 2*n; i++)        {            if(col[id[i]] == goal)                printf("%d%c ", i/2, i&1 ? 'h' : 'w');        }        puts("");    }    return 0;}




原创粉丝点击