bzoj 1997 Planar [并查集] [二分图判定] [2-SAT]

19 篇文章 0 订阅
5 篇文章 0 订阅

1997: [Hnoi2010]Planar

Time Limit: 10 Sec Memory Limit: 64 MB
Submit: 1509 Solved: 585

Description
这里写图片描述
Input
这里写图片描述
Output
这里写图片描述
Sample Input
2
6 9
1 4
1 5
1 6
2 4
2 5
2 6
3 4
3 5
3 6
1 4 2 5 3 6
5 5
1 2
2 3
3 4
4 5
5 1
1 2 3 4 5

Sample Output
NO
YES

Source
Day1


三种做法,并查集,二分图判定,2-SAT
前两种就是上一道题的check,但是注意要剪枝,m<=3n-6时才有可能成为平面图。


2-SAT解法:

这道题把所有边拆成两个,分别代表在里面和在外面,然后又里面和外面取且一定取一个,那么连上四条边即可。。
又因为这道题只用判断可行性,那么就只用缩点之后判断是否有冲突即可,如果要输出方案,那么就需要按照拓扑序的反序进行点的确定,然后依次标记可选的点,传递不能选的标记,然后该图的染色就是2-SAT问题的解。
另外要注意提前break的时候要保证读完该组数据,不然RE…

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<climits>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
const int INF=0x3f3f3f3f;
const int maxnode = 205;
const int maxn = 605;
const int maxm = 10005;
struct Edge
{
    int to,next;
}edge[maxn*maxn<<2]; // 4 edges per condition
int head[maxn<<1]; // divided
int maxedge;
inline void addedge(int u,int v)
{
    edge[++maxedge] = (Edge) { v,head[u] };
    head[u] = maxedge;
}
struct Road
{
    int u,v;
}road[maxn],save[maxm];
bool g[maxnode][maxnode];
int a[maxnode],order[maxnode];
int n,m,tmp;
void init()
{
    memset(g,0,sizeof(g));
    memset(head,-1,sizeof(head));
    maxedge = -1;
    m=0;
    for(int i=1;i<=tmp;i++) scanf("%d%d",&save[i].u,&save[i].v);
    for(int i=1;i<=n;i++) scanf("%d",a+i),order[a[i]]=i;
    if(tmp>n*3-6) return; // spacial judge after scanning!!
    for(int i=1;i<n;i++) g[a[i]][a[i+1]] = g[a[i+1]][a[i]] = true;
    g[a[1]][a[n]] = g[a[n]][a[1]] = true;
    for(int i=1;i<=tmp;i++) if(!g[save[i].u][save[i].v]) road[++m]=save[i];
}
inline bool cross(int i,int j) // cases of including node n,1 is considered another way
{
    int x1=order[road[i].u],y1=order[road[i].v];
    int x2=order[road[j].u],y2=order[road[j].v];
    if(x1>y1) swap(x1,y1);
    if(x2>y2) swap(x2,y2);
    return (x1<x2 && x2<y1 && y1<y2) || (x2<x1 && x1<y2 && y2<y1);
}
int dfn[maxn<<1],sccno[maxn<<1];
bool insta[maxn<<1];
int scc_cnt,dfs_clock;
stack <int> sta;
int dfs(int u)
{
    int lowu=dfn[u]=++dfs_clock;
    sta.push(u); insta[u]=true;
    for(int i=head[u];~i;i=edge[i].next)
    {
        int v = edge[i].to;
        if(!dfn[v])
        {
            int lowv = dfs(v);
            smin(lowu,lowv);
        }
        else if(insta[v]) smin(lowu,dfn[v]);
    }
    if(lowu>=dfn[u])
    {
        scc_cnt++;
        int t;
        do
        {
            t=sta.top(); sta.pop(); insta[t]=false;
            sccno[t]=scc_cnt; // if needed to print , note the nodes of SCC
        }while(t^u);
    }
    return lowu;
}
void Tarjan()
{
    memset(dfn,0,sizeof(dfn));
    memset(sccno,0,sizeof(sccno));
    dfs_clock = scc_cnt = 0;
    for(int i=1;i<=m;i++)
    {
        if(!dfn[i]) dfs(i);
        if(!dfn[i+m]) dfs(i+m);
    }
}
inline bool check()
{
    for(int i=1;i<=m;i++)
        if(sccno[i] == sccno[i+m]) return false;
    return true;
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("planar.in","r",stdin);
    freopen("planar.out","w",stdout);
    #endif
    int cas;
    scanf("%d",&cas);
    while(cas--)
    {
        scanf("%d%d",&n,&tmp);
        init();
        if(tmp>n*3-6) // judge after scanning all data!!
        {
            printf("NO\n");
            continue;
        }
        for(int i=1;i<m;i++)
            for(int j=i+1;j<=m;j++)
                if(cross(i,j))
                {
                    addedge(i,j+m);
                    addedge(j,i+m);
                    addedge(i+m,j);
                    addedge(j+m,i);
                }
        Tarjan();
        if(check()) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值