图论--2-SAT--Ligthoj 1407 Explosion 三元关系枚举
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. 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'.
题目大意:
有一个机器产生m个限制,限制有4种:
1. x or y 至少有1个人参加
2. x不参加 则 y必须不参加,(隐含 y参加 x必须参加)
3. x or y 至少有1个人不参加
4. x & y 同时参加 或者不参加
有 k 个人 进行投票,有2种类别
1. x y z 至少有一个人参加
2. x y z 至少有一个人不参加
有n 个人参加会议,m 个机器限制,k个人投票 (3 ≤ n ≤ 1000, 0 ≤ m ≤ 2000, 0 ≤ k ≤ 5)
解题思路:肯定是 2-sat,k比较小直接枚举3^k。剩下的就是一个模板。
建图:
二元关系直接建图,三元关系枚举成二元判断是否可行,不可行,继续枚举,可行输出答案,知道都枚举完,就无解。反正是一个NP完全问题,所以枚举无伤大雅。二元建图,看我博客2-SAT详解,一看就明白。戳死我
#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;
}
图论--2-SAT--Ligthoj 1407 Explosion 三元关系枚举的更多相关文章
- BZOJ.3498.[PA2009]Cakes(三元环 枚举)
题目链接 感觉我可能学的假的(复杂度没问题,但是常数巨大). 一个比较真的说明见这儿:https://czyhe.me/blog/algorithm/3-mem-ring/3-mem-ring/. \ ...
- ER 和 数据库关系模式
http://lianghuanyue123.blog.163.com/blog/static/130423244201162011850600/ 我们眼下所接触的数据库基本上是关系数据库,关系数据库 ...
- 【WIP_S9】图论算法
创建: 2018/06/01 图的概念 有向边 有向图 无向边 无向图 点的次数: 点连接的边的数量 闭路: 起点和重点一样 连接图: 任意两点之间都可到达 无闭路有向图: 没有闭路的有向图 森林: ...
- 什么是关系图 (ERD)?
首先,什么是实体关系图? 实体关系图,也称为ERD,ER图或ER模型,是一种用于数据库设计的结构图.一个ERD包含不同的符号和连接器,它们可视化两个重要信息:系统范围内的主要实体,以及这些实体之间的相 ...
- 我所学的c语言
c语言结构 #include <stdio.h> int main(){ /* 我的第一个 C 程序 */ printf("Hello, World! \n" ...
- lecture4-神经网络在语言上的应用
Hinton第四课 这一课主要介绍神经网络在语言处理上应用,而主要是在文本上,并附上了2003年Bengio 等人的19页的论文<A Neural Probabilistic Language ...
- QQ 腾讯QQ(简称“QQ”)是腾讯公司开发的一款基于Internet的即时通信(IM)软件
QQ 编辑 腾讯QQ(简称“QQ”)是腾讯公司开发的一款基于Internet的即时通信(IM)软件.腾讯QQ支持在线聊天.视频通话.点对点断点续传文件.共享文件.网络硬盘.自定义面板.QQ邮箱等多种功 ...
- bzoj1109
我们设f[i]为保留第i个木块最多的符合未知数 显然f[i]=max(f[j])+1 满足i>j a[i]>a[j] i-j>=a[i]-a[j] 我们把最后一个式子变成a[i]-i ...
- DBA词典:数据库设计常用词汇中英文对照表
1. Access method(访问方法):此步骤包括从文件中存储和检索记录. 2. Alias(别名):某属性的另一个名字.在SQL中,可以用别名替换表名. 3. Alternate keys(备 ...
随机推荐
- MTK Android 设置下添加一级菜单[ZedielPcbTest]
功能描述:Android7.1.2 设置下添加一级菜单[ZedielPcbTest],点击ZedielPcbTest,启动ZedielPcbTest.apk应用. 编译:需要在out目录删除Settt ...
- Android 启动一个Activity的几种方式
启动一个Activity的几种方式在Android中我们可以通过下面两种方式来启动一个新的Activity,注意这里是怎么启动,而非启动模式!分为显示启动和隐式启动! 1.显式启动,通过包名来启动,写 ...
- python 网络编程---粘包
一.什么是粘包?(只有在TCP中有粘包现象,在UDP中永远不会粘包) 黏包不一定会发生. 如果发生 了:1.可能是在客户端已经粘了 2.客户端没有粘,可能是在服务端粘了. 所谓的粘包问题:主要是是因为 ...
- wireshark抓包实战(八),专家分析
专家分析是干什么的?它可以帮你统计当前所抓包中丢包.错包等等的出现概率 其中关键字如下: error ===> 出错包 warning ===> 警告包 note ===> 注意包 ...
- Linux 下普通用户切换root超级管理员用户的几种方法
1.在命令行下输入:sudo su ,之后会提示你输入密码 2.此时输入你之前设定的密码既可: 3.但有时会提示你该普通用户不在sudoers文件里 4.此时可以使用以下命令来切换root用户权限:s ...
- c++动态数组的优点,创建和删除
动态数组可以有两种使用方式: 1:不能预先知道数组的大小使用动态数组 传统数组(静态数组)是需要在程序运行前,就指定大小,比如说 int i = 10; int a[i]; 这种就是不合法的. 因为函 ...
- Win10 cmd的ssh命令连接linux虚拟机
其实就是一个小发现了~ 闲的没事的时候在cmd里面敲了ssh命令,居然提示是一个命令,貌似以前是没有这功能的.然后就打开虚拟机试试能不能远程连接.没想到还成功了~ 有了这功能就省得安装专门的远程连接工 ...
- Centos7_Root密码重置
原因: 最近出去见女朋友,竟然忘了Root用户的密码,此时考验linux基础扎不扎实的时候到了... 操作步骤: 解释补充: mount -o remountr,w / #修改根目录文件系统的权限,实 ...
- FreeRTOS操作系统工程建立和操作系统的概念
一.建立工程步骤如下: 二.详细步骤流程如下: 1.新建工程文件夹,然后在里面建立如下几个文件: 2.使用keil5建立工程: a.建立工程: b.添加内核文件: 3.建立文件分组: 4.创建main ...
- T - Nash Matrix CodeForces - 1316D
题意: 输入n行数,没行由2*n个数,表示一个坐标(x,y). 如果x和y==-1表示从该点(i,j)出发,按照构造的前移动不会停下. 否则就要到点(x,y)处停下. 题解: 首先处理-1 枚举每个 ...