天天看点

Light OJ 1407 - Explosion 【2-sat之 3布尔变量的处理 — 枚举所有状态判断是否存在可行解 + 反向输出可行解】【好题】InputOutputSample InputOutput for Sample InputNote

1407 - Explosion

Light OJ 1407 - Explosion 【2-sat之 3布尔变量的处理 — 枚举所有状态判断是否存在可行解 + 反向输出可行解】【好题】InputOutputSample InputOutput for Sample InputNote
PDF (English) Statistics Forum
Time Limit: 3 second(s) Memory Limit: 32 MB

Planet Krypton is about to explode. The inhabitants of this planet have to leave the planet immediately. But the problem is that, still some decisions have to be made - where to go, how to go etc. So, the council of Krypton has invited some of the people to meet in a large hall.

There are n people in planet Krypton, for simplicity they are given ids from 1 to n. The council uses a super computer named Oracle to call them in the meeting. Oracle has four types of messages for invitation. 

Light OJ 1407 - Explosion 【2-sat之 3布尔变量的处理 — 枚举所有状态判断是否存在可行解 + 反向输出可行解】【好题】InputOutputSample InputOutput for Sample InputNote

The message format is type x y, where x and y are two different person's ids and type is an integer as follows:

1.      1 x y means that either x or y should be present in the meeting.

2.      2 x y means that if x is present, then no condition on y, but if x is absent y should be absent

3.      3 x y means that either x or y must be absent.

4.      4 x y means that either x or y must be present but not both.

Each member of the council has an opinion too. The message format is type x y z, where x, y and z are three different person's ids and type is an integer as follows:

1.      1 x y z means that at least one of x, y or z should be present

2.      2 x y z means that at least one of x, y or z should be absent

Now you have to find whether the members can be invited such that every message by oracle and the council members are satisfied.

Input

Input starts with an integer T (≤ 200), denoting the number of test cases.

Each case starts with a blank line. Next line contains three integers n, m and k (3 ≤ n ≤ 1000, 0 ≤ m ≤ 2000, 0 ≤ k ≤ 5) where m means the number of messages by oracle, k means the total members in the council. Each of the next m lines will contain a message of Oracle in the format given above. Each of the next k lines will contain a message of a council member. You can assume that all the ids given are correct.

Output

For each case, print the case number and whether it's possible to invite the people such that all the messages are satisfied. If it's not possible, then print'Impossible.' in a single line. Otherwise, print 'Possible' and the number of invited people and the ids of the invited people in ascending order. Print the line leaving a single space between fields. Terminate this line with a '.'. See the samples for more details. There can be multiple answers; print any valid one.

Sample Input

Output for Sample Input

3

3 2 1

3 2 1

1 2 3

1 1 2 3

4 4 1

2 2 1

4 1 2

4 1 3

4 1 4

2 2 3 4

4 5 0

3 1 2

2 2 3

2 2 4

2 1 2

2 2 1

Case 1: Possible 2 1 3.

Case 2: Impossible.

Case 3: Possible 0.

Note

This is a special judge problem; wrong output format may cause 'Wrong Answer'.

PROBLEM SETTER: JANE ALAM JAN SPECIAL THANKS: DEREK KISMAN

题意:要从N个人(编号从1到N)里面选一些人参加会议,先给出M个2布尔变量的约束条件和K个3布尔变量的约束条件。让你选出若干个人参加会议,若不存在方案输出Impossible。否则输出Possible并且在后面输出参加会议的人数以及他们的编号(编号从小到大)。

2布尔变量约束如下

1,x y  表示x和y至少去一个 2,x y  表示 x去的话y怎么样都可以,但是若x不去那么y一定不去 3,x y  表示x和y至少一个不去 4,x y  表示x和y必须去一个且只能去一个

3布尔变量约束如下

1,x y z  表示x、y和z至少去一个 2,x y z  表示x、y和z至少一个不去

快一天了,╮(╯▽╰)╭。还没有做过带有3个布尔变量的题目,一开始天真的用2-sat思维做,结果WA到死。。。最后发现K值最多为5,还能怎样?直接暴力枚举所有状态吧。几个小时没白过,至少学会了处理3个布尔变量的情况。 O(∩_∩)O~~

这里只说一下2布尔变量的建图

1,x y  表示x和y至少去一个 addEdge(y + N, x); //y不去x去

addEdge(x + N, y);//x不去y去

2,x y  表示 x去的话y怎么样都可以,但是若x不去那么y一定不去 addEdge(y, x); //注意 若y去则x是一定去的

addEdge(x + N, y + N); //x不去y也不去

3,x y  表示x和y至少一个不去 addEdge(x, y + N); //x去 y不去

addEdge(y, x + N);//y去 x不去

4,x y  表示x和y必须去一个且只能去一个 addEdge(x, y + N);

addEdge(y, x + N);

addEdge(x + N, y);

addEdge(y + N, x);

至于3 个布尔变量状态的枚举,强烈建议模拟一下,真的不好讲。毕竟我也是初学3布尔变量的渣渣,o(╯□╰)o

AC代码:

#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#define MAXN 2000+100
#define MAXM 10000+100
using namespace std;
struct Edge
{
    int from, to, next;
};
Edge edge[MAXM], Redge[MAXM];
int head[MAXN], edgenum;
int Rhead[MAXN], Redgenum;//这些数组用于copy 这样就不用再次建已经确定的边了
struct Node
{
    int op, x, y, z;
};
Node num[5];
int low[MAXN], dfn[MAXN];
int sccno[MAXN], scc_cnt;
int dfs_clock;
stack<int> S;
bool Instack[MAXN];
int N, M, K;
void init()
{
    edgenum = 0;
    memset(head, -1, sizeof(head));
}
void addEdge(int u, int v)
{
    Edge E = {u, v, head[u]};
    edge[edgenum] = E;
    head[u] = edgenum++;
}
void input()
{
    int x, y, z, op;
    while(M--)
    {
        scanf("%d%d%d", &op, &x, &y);
        if(op == 1)//x和y至少去一个
        {
            addEdge(y + N, x);//y不去x去
            addEdge(x + N, y);//x不去y去
        }
        else if(op == 2)
        {
            addEdge(y, x);//注意 若y去则x是一定去的
            addEdge(x + N, y + N);//x不去y也不去
        }
        else if(op == 3)//x和y至少一个不去
        {
            addEdge(x, y + N);//x去 y不去
            addEdge(y, x + N);//y去 x不去
        }
        else//两个人只能去一个
        {
            addEdge(x, y + N);
            addEdge(y, x + N);
            addEdge(x + N, y);
            addEdge(y + N, x);
        }
    }

    for(int i = 0; i < K; i++)
        scanf("%d%d%d%d", &num[i].op, &num[i].x, &num[i].y, &num[i].z);
    memcpy(Rhead, head, sizeof(head));
    memcpy(Redge, edge, sizeof(edge));
    Redgenum = edgenum;
}
void tarjan(int u, int fa)
{
    int v;
    low[u] = dfn[u] = ++dfs_clock;
    S.push(u);
    Instack[u] = true;
    for(int i = head[u]; i != -1; i = edge[i].next)
    {
        v = edge[i].to;
        if(!dfn[v])
        {
            tarjan(v, u);
            low[u] = min(low[u], low[v]);
        }
        else if(Instack[v])
            low[u] = min(low[u], dfn[v]);
    }
    if(low[u] == dfn[u])
    {
        scc_cnt++;
        for(;;)
        {
            v = S.top(); S.pop();
            Instack[v] = false;
            sccno[v] = scc_cnt;
            if(v == u) break;
        }
    }
}
void find_cut(int l, int r)
{
    memset(low, 0, sizeof(low));
    memset(dfn, 0, sizeof(dfn));
    memset(sccno, 0, sizeof(sccno));
    memset(Instack, false, sizeof(Instack));
    dfs_clock = scc_cnt = 0;
    for(int i = l; i <= r; i++)
        if(!dfn[i]) tarjan(i, -1);
}
int fp[MAXN];//建立SCC间的映射
bool two_sat()//判断当前情况是否成立
{
    find_cut(1, 2*N);
    for(int i = 1; i <= N; i++)
    {
        if(sccno[i] == sccno[i+N])
            return false;
        else
        {
            fp[sccno[i]] = sccno[i+N];
            fp[sccno[i+N]] = sccno[i];
        }
    }
    return true;
}
int k = 1;
vector<int> G[MAXN];//缩点后新图
int in[MAXN];//记录SCC入度
void suodian()//缩点
{
    for(int i = 1; i <= scc_cnt; i++) G[i].clear(), in[i] = 0;
    for(int i = 0; i < edgenum; i++)
    {
        int u = sccno[edge[i].from];
        int v = sccno[edge[i].to];
        if(u != v)
            G[v].push_back(u), in[u]++;
    }
}
int color[MAXN];//染色
void toposort()//拓扑染色
{
    queue<int> Q;
    memset(color, -1, sizeof(color));
    for(int i = 1; i <= scc_cnt; i++) if(in[i] == 0) Q.push(i);
    while(!Q.empty())
    {
        int u = Q.front();
        Q.pop();
        if(color[u] == -1)
        {
            color[u] = 1;
            color[fp[u]] = 0;
        }
        for(int i = 0; i < G[u].size(); i++)
        {
            int v = G[u][i];
            if(--in[v] == 0)
                Q.push(v);
        }
    }
}
void solve()
{
    int State = (int)pow(3, K);//总状态数
    bool flag = false;
    for(int S = 0; S < State; S++)//这里状态下标从1开始或从2开始 都不会影响 注意取值就行了
    {
        memcpy(head, Rhead, sizeof(Rhead));//还原数组
        memcpy(edge, Redge, sizeof(Redge));
        edgenum = Redgenum;
        int T = S;
        for(int i = 0; i < K; i++)//继续枚举状态建图
        {
            int s;
            switch(T % 3)//需要仔细琢磨 这个过程
            {
                case 0: s = num[i].x; break;
                case 1: s = num[i].y; break;
                case 2: s = num[i].z; break;
            }
            T /= 3;
            if(num[i].op == 1)
                addEdge(s + N, s);//s一定去
            else
                addEdge(s, s + N);//s一定不去
        }
        if(two_sat())//成立
        {
            flag = true;
            break;
        }
    }
    printf("Case %d: ", k++);
    if(!flag)
    {
        printf("Impossible.\n");
        return ;
    }
    printf("Possible");
    //输出可行解
    suodian();
    toposort();
    int ans = 0;
    for(int i = 1; i <= N; i++)
    {
        if(color[sccno[i]] == 1)
            ans++;
    }
    printf(" %d", ans);
    for(int i = 1; i <= N; i++)
        if(color[sccno[i]] == 1)
            printf(" %d", i);
    printf(".\n");
}
int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%d%d", &N, &M, &K);
        init();
        input();
        solve();
    }
    return 0;
}