/*
2-sat
题意:一个环上有n个点,有m对点之间有连接,连接可以在环内,也可以在环外,问这些连接是否相交
重点还是在建图,每个连接作为一个对象,他有两个状态,在环内,在环外
若两连接相交,则必是一个在环内,一个在环外,
如a、b相交,包含同在环内相交,同在环外相交,则
同在内相交:
a在内(a*2),那么b必在外(b*2+1)
b在内(b*2),那么a必在外(a*2+1)
同在外相交:
a在外(a*2+1),那么b必在内(b*2)
b在外(b*2+1),那么a必在内(a*2)
如此建边即可
*/
2-sat
题意:一个环上有n个点,有m对点之间有连接,连接可以在环内,也可以在环外,问这些连接是否相交
重点还是在建图,每个连接作为一个对象,他有两个状态,在环内,在环外
若两连接相交,则必是一个在环内,一个在环外,
如a、b相交,包含同在环内相交,同在环外相交,则
同在内相交:
a在内(a*2),那么b必在外(b*2+1)
b在内(b*2),那么a必在外(a*2+1)
同在外相交:
a在外(a*2+1),那么b必在内(b*2)
b在外(b*2+1),那么a必在内(a*2)
如此建边即可
*/
#include <stdio.h>
#include <string.h>
#include <stack>
using namespace std;
struct edge
{
int ansi;
int v[1000000];
int next[1000000];
int head[1000000];
edge()
{
clear();
}
void clear()
{
ansi = 1;
memset(head, 0, sizeof(head));
}
void add(int u, int w)
{
v[ansi] = w;
next[ansi] = head[u];
head[u] = ansi;
ansi++;
}
} e1;
int dfn[5000], low[5000];
int n, m, index, scc;
stack<int>ss;
int instack[5000], belong[5000];
struct link
{
int x, y;
} l[1200];
void tarjan(int u)//tarjan求强连通分量
{
int i, v;
dfn[u] = low[u] = index++;
instack[u] = 1;
ss.push(u);//之前误写成队列
for (i = e1.head[u]; i; i = e1.next[i])
{
v = e1.v[i];
if (dfn[v] == 0)
{
tarjan(v);
if (low[v] < low[u])
low[u] = low[v];
}
else if (instack[v] && dfn[v] < low[u])
low[u] = dfn[v];
}
if (low[u] == dfn[u])
{
do
{
v = ss.top();
ss.pop();
instack[v] = 0;
belong[v] = scc; //标记所属强连通分量的标号
}
while (v != u);
scc++;
}
}
int jiao(int j, int i)
{
if (l[j].x < l[i].x && l[i].x < l[j].y && l[j].y < l[i].y)
return 1;
if (l[i].x < l[j].x && l[j].x < l[i].y && l[i].y < l[j].y)
return 1;
return 0;
}
int main()
{
e1.clear();
int i, j;
scanf("%d%d", &n, &m);
for (i = 0; i < m; i++)
{
scanf("%d%d", &l[i].x, &l[i].y);
if (l[i].x > l[i].y)
{
int tmp = l[i].x;
l[i].x = l[i].y;
l[i].y = tmp;
}
for (j = 0; j < i; j++)
{
if (jiao(j, i))
{
e1.add(i * 2, j * 2 + 1);
e1.add(j * 2, i * 2 + 1);
e1.add(i * 2 + 1, j * 2);
e1.add(j * 2 + 1, i * 2);
}
}
}
index = 1; //dfn[]标记
scc = 0; //强连通分量个数,从0开始计数
memset(dfn, 0, sizeof(dfn));
memset(instack, 0, sizeof(instack));
for (i = 0; i < 2 * m; i++)
{
if (!dfn[i])
tarjan(i);
}
for (i = 0; i < m; i++)
{
if (belong[2 * i] == belong[2 * i + 1])
break;
}
if (i == m)
printf("panda is telling the truth...\n");
else printf("the evil panda is lying again\n");
return 0;
}
-------------------————————————————————————————————————————————————————————————
题意:一个圆盘上有n个点,编号顺次为0……n-1,现在给出m条线段,这些线段可以在盘的正面,也可以在反面,要求判断最终是否有线段不相交的安排,使满足要求.
令第i条线(i从0开始)在正面为第2i个顶点,在反面为第2i+1个顶点.若第i条线和第j条线构成一个四边形的两条对角线,则在2i和2j+1之间连一条双向边,2i+1和2j之间连一条双向边.这样建图后求强连通分量,看点2i和点2i+1是否在同一个强连通分量中,如果是在同一强连通分量中,就说明不满足要求,如果没有则满足.
首先,我们知道若第i条线和第j条线构成一个四边形的两条对角线,则2i和2j不相容,又2i和2i+1中必须出现一个,且2j和2j+1中也必须出现一个,所以可以说2i+1和2j+1中至少出现一个,换句话说, 从2i到2j+1连一条有向边表示如果选择了2i就必须选择2j+1,从2j到2i+1连一条有向边表示如果选择了2j就必须选择2i+1,同理对于2i+1和2j+1的不相容也可以连两条有向边,所以最后跟上面的组成了两条双向边.
可能是由于使用了STL中的vector吧,所以速度很慢.
#include<iostream>
#include<string>
#include<cstdlib>
#include<vector>
#include<algorithm>
using namespace std;
const int MAXN = 1005;
const int MAXM = 505;
int dfn[MAXN], low[MAXN], stack[MAXN], belong[MAXN], a[MAXM], b[MAXM];
int top, cnt, n, m, ans;
bool instack[MAXN];
vector<int>tree[MAXN];
void Input()
{
int i, j;
for (i = 0; i < 2 * m; i++)
tree[i].clear();
scanf("%d%d", &n, &m);
for (i = 0; i < m; i++)
{
scanf("%d%d", &a[i], &b[i]);
if (a[i] > b[i]) //节点是顺时针排列的
swap(a[i], b[i]);
}
for (i = 0; i < m; i++)
for (j = i + 1; j < m; j++) //建图
if ((a[i] < a[j] && b[i] < b[j] && a[j] < b[i]) || (a[i] > a[j] && b[i] > b[j] && a[i] < b[j])) //编号为i,j的两条线段相交
{
tree[2 * i].push_back(2 * j + 1);
tree[2 * i + 1].push_back(2 * j);
tree[2 * j].push_back(2 * i + 1);
tree[2 * j + 1].push_back(2 * i);
}
}
void DFS(int i)
{
int j, k;
dfn[i] = low[i] = ++ans;
instack[i] = true;
stack[++top] = i;
for (k = 0; k < tree[i].size(); k++)
{
j = tree[i][k];
if (!dfn[j])
{
DFS(j);
if (low[j] < low[i])
low[i] = low[j];
}
else if (instack[j] && dfn[j] < low[i])
low[i] = dfn[j];
}
if (dfn[i] == low[i])
{
cnt++;
do
{
j = stack[top--];
instack[j] = false;
belong[j] = cnt;
}
while (j != i);
}
}
void Tarjan() //Tarjan算法求解强连通分量
{
int i;
top = cnt = ans = 0;
memset(dfn, 0, sizeof(dfn));
for (i = 0; i < 2 * m; i++)
if (!dfn[i])
DFS(i);
}
bool Judge() //判断是否存在可行解
{
int i;
for (i = 0; i < 2 * m; i += 2)
if (belong[i] == belong[i + 1]) //即不相容的两条边在同一强连通分量中
return false;
return true;
}
int main()
{
Input();
Tarjan();
if (Judge())
printf("panda is telling the truth...\n");
else
printf("the evil panda is lying again\n");
system("pause");
return 0;
}