Ikki's Story IV-Panda's Trick - 竞赛题解

也算是2-sat学习的一个节点吧

终于能够自己解决一道2-sat的题了


·题目

一个圆上有n个点按顺时针编号为 0~n-1 ,有m条边连接一些点(无自环无重边),每条边要么在圆内,要么在圆外。求是否存在一种方案(方案即每条边在圆内还是圆外)使得边之间不存在交点。

如果存在,输出"panda is telling the truth...",否则输出"the evil panda is lying again"。

(额……多组数据,规模一般,2-sat能过就对了)


·解析

由于一条边要么在圆内,要么在圆外,这就相当于两个状态,而每条边就是一个变量——这就形成了一个2-sat问题。

首先处理一下输入:因为点的个数其实没有任何影响( ̄▽ ̄)",我们只需要保存边,显然边 (u,v) 等同于 (v,u) (画图即可证明),为了方便,我们在读入边(a,b)时,稍微处理一下,使a<b。

简单地推理一下,如果两条边(a,b),(c,d)相交,则必然满足 \(a<c<b<d\ ||\ b<a<d<c\),且(a,b),(c,d)同在圆内/外

根据这个简单的性质,我们定义点 \((i<<1)\) 和 \((i<<1|1)\) 分别表示边 i 在圆内、圆外。如果 边i 和 边j 相交(用上面的性质判断),则 (i<<1) 和 (j<<1|1) 、(i<<1|1) 和 (j<<1) 之间都存在一条无向边;同时我们还要存一个反图,后面求强连通分量要用。

建完图后,我们先对所有点进行一次DFS,当某一个点的DFS退出时,将它压入栈内(这里不好解释,看代码就明白了(●'◡'●))。再从栈顶取出点,从该点开始DFS,接下来的所有能通过反图边到达的点都和起点属于同一个强连通分量,直到栈空。这里建议手写栈,STL的没有必要QwQ

最后判断对于每个 边i(即变量),点(i<<1) 和 点(i<<1|1) 是否处于一个连通块内,如果存在这样的i,则不成立,否则成立~

这样就做完了,没什么细节,就是注意存反图的时候别写丑了。


·源代码

(尽量把注释写的详细一点,希望大家能够看懂( ̄︶ ̄*))

/*Lucky_Glass*/
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=500;
struct EDGE{
int to,nxt;
EDGE(){}
EDGE(int _to,int _nxt):to(_to),nxt(_nxt){};
}edg[(N*2)*(N*2)*2+5][2];
int adj[N*2+5][2],edgtot[2];
void AddEdge(int u,int v,int knd){
edg[++edgtot[knd]][knd]=EDGE(v,adj[u][knd]);
adj[u][knd]=edgtot[knd];
}
int n;
int inp[N*2+5][2],mem[N*2+5],blk[N*2+5];
bool vis[N*2+5];
void DFS1(int u){ //这里是原图
vis[u]=true;
for(int i=adj[u][0];i!=-1;i=edg[i][0].nxt){
int v=edg[i][0].to;
if(vis[v]) continue;
DFS1(v);
}
mem[++mem[0]]=u; //退出DFS时压栈
}
void DFS2(int u,int id){ //这里是反图
vis[u]=true;blk[u]=id; //id是连通块编号,blk[u]表示u所属连通块
for(int i=adj[u][1];i!=-1;i=edg[i][1].nxt){
int v=edg[i][1].to;
if(vis[v]) continue;
DFS2(v,id);
}
}
bool Solve(){
memset(vis,false,sizeof vis);
for(int i=0;i<2*n;i++) //DFS遍历每一个点,求出DFS退出栈
if(!vis[i]) //O(n)-每个点只访问一次
DFS1(i);
//mem[]是手写栈,mem[0]是栈大小
memset(vis,false,sizeof vis);
for(int i=2*n;i>0;i--) //实际上是从栈顶元素,总共2*n个点
if(!vis[mem[i]])
DFS2(mem[i],i);
for(int i=0;i<n;i++) //判断每一条边
if(blk[i<<1]==blk[i<<1|1]) //只要一条边不符合条件
return false;
return true;
}
void Clear(){
// memset(edg,0,sizeof edg); 这里没必要清空
memset(adj,-1,sizeof adj);
edgtot[0]=edgtot[1]=0; //用链表存图的reader们注意这个
}
int main(){
while(~scanf("%*d%d",&n)){
Clear(); //注意清空图!
for(int i=0;i<n;i++){
scanf("%d%d",&inp[i][0],&inp[i][1]);
if(inp[i][0]>inp[i][1]) swap(inp[i][0],inp[i][1]);
}
//choose i -> i<<1
//not choose i -> i<<1|1
for(int i=0;i<n;i++)
for(int j=i+1;j<n;j++){
bool A=(inp[i][0]<inp[j][0] && inp[j][0]<inp[i][1] && inp[i][1]<inp[j][1]);
bool B=(inp[j][0]<inp[i][0] && inp[i][0]<inp[j][1] && inp[j][1]<inp[i][1]);
//判断是否相交
if(A||B){ //同时处理正反图,AddEdge(u,v,0/1)表示在正/反图中连接u->v的边,因为是无向边,所以需要相互连
AddEdge(i<<1,j<<1|1,0);AddEdge(j<<1|1,i<<1,1);
AddEdge(i<<1|1,j<<1,0);AddEdge(j<<1,i<<1|1,1);
AddEdge(j<<1,i<<1|1,0);AddEdge(i<<1|1,j<<1,1);
AddEdge(j<<1|1,i<<1,0);AddEdge(i<<1,j<<1|1,1);
}
}
bool res=Solve();
printf("%s\n",res? "panda is telling the truth...":"the evil panda is lying again");
}
return 0;
}

\(\mathcal THE\ END\)

\(\mathcal Thanks\ for\ reading!\)

竞赛题解 - Ikki's Story IV-Panda's Trick的更多相关文章

  1. poj 3207 Ikki's Story IV - Panda's Trick (2-SAT)

    http://poj.org/problem?id=3207 Ikki's Story IV - Panda's Trick Time Limit: 1000MS   Memory Limit: 13 ...

  2. POJ 3207 Ikki's Story IV - Panda's Trick

    Ikki's Story IV - Panda's Trick Time Limit: 1000MS   Memory Limit: 131072K Total Submissions: 7296   ...

  3. POJ 3207 Ikki's Story IV - Panda's Trick(2-sat问题)

    POJ 3207 Ikki's Story IV - Panda's Trick(2-sat问题) Description liympanda, one of Ikki's friend, likes ...

  4. 【POJ3207】Ikki's Story IV - Panda's Trick

    POJ 3207 Ikki's Story IV - Panda's Trick liympanda, one of Ikki's friend, likes playing games with I ...

  5. POJ 3207 Ikki's Story IV - Panda's Trick (2-sat)

    Ikki's Story IV - Panda's Trick Time Limit: 1000MS   Memory Limit: 131072K Total Submissions: 6691   ...

  6. POJ3207 Ikki's Story IV - Panda's Trick 【2-sat】

    题目 liympanda, one of Ikki's friend, likes playing games with Ikki. Today after minesweeping with Ikk ...

  7. Ikki's Story IV - Panda's Trick

    poj3207:http://poj.org/problem?id=3207 题意::平面上有一个圆,圆的边上按顺时针放着0..n-1共n个点.现在要连m条边,比如a,b,那么a到b可以从圆的内部连接 ...

  8. poj3207 Ikki's Story IV - Panda's Trick 2-sat问题

    ---题面--- 题意:给定一个圈,m条边(给定),边可以通过外面连,也可以通过里面连,问连完这m条边后,是否可以做到边两两不相交 题解: 将连里面和连外面分别当做一种决策(即每条边都是决策点), 如 ...

  9. POJ3207 Ikki's Story IV – Panda's Trick

    Time Limit: 1000MS   Memory Limit: 131072K Total Submissions: 9426   Accepted: 3465 Description liym ...

  10. 图论--2-SAT--POJ Ikki's Story IV - Panda's Trick

    Description liympanda, one of Ikki's friend, likes playing games with Ikki. Today after minesweeping ...

随机推荐

  1. 鼠标事件-拖拽(不能拖出窗口的div)

    <!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title>& ...

  2. cnpm 安装

    国内npm 安装比较慢,可选择cnpm npm install -g cnpm --registry=https://registry.npm.taobao.org

  3. 04_zookeeper的watcher机制

    [watcher简述] * zk针对每个节点的操作,都会有一个监督者:watcher * 当监控的某个对象(znode)发生了变化,则出发watcher * zk中的watcher是一次性的,出发后立 ...

  4. windows 下进程与线程的遍历

    原文:http://www.cnblogs.com/Apersia/p/6579376.html 在Windows下进程与线程的遍历有好几种方法. 进程与线程的遍历可以使用<TlHelp.h&g ...

  5. Android解析ActivityManagerService(二)ActivityTask和Activity栈管理

    前言 关于AMS,原计划是只写一篇文章来介绍,但是AMS功能繁多,一篇文章的篇幅远远不够.这一篇我们接着来学习与AMS相关的ActivityTask和Activity栈管理. 1.ActivitySt ...

  6. c 读取整个文件内容

    char* textFileRead(char* filename){char* text;FILE *pf = fopen(filename,"r");fseek(pf,0,SE ...

  7. dubbo学习总结一 API

    API 一般用来暴露接口 项目分层一般是 api + entity + enums + model 就是接口加上一些实体之类的东西

  8. maven学习(七)后续扩展、资料

    写这几篇博客的来源是 "maven实战 + 网上的博客 + 平时使用的心得 ".记录的都是比较常用的东西,也有一些只做了大概了解.或者干脆直接略过,在这里做一下总结,如果有需要在进 ...

  9. CSS/LESS tips and snippets

    如何style line-through? <style type="text/css"> span.inner { color: green; } span.oute ...

  10. Log4net (Log for .net)

    Log4net (Log for .net) 开源的记录日志的组件,是从java的Log4J移植到.net 日志文件存放的位置 在项目新建“App_Code”和“App_Data”,该文件夹客户端无法 ...