P1444 [USACO1.3] 虫洞 wormhole

题目描述

Farmer John 周末进行高能物理实验的结果却适得其反,导致 nn 个虫洞出现在农场上,农场是一个二维平面,没有两个虫洞处于同一位置。

根据他的计算,FJ 知道他的虫洞两两配对,形成 n22n​ 对配对。例如,如果 AA 和 BB 的虫洞连接成一对,进入虫洞 AA 的任何物体将从虫洞 BB 出去,方向不变;反之亦然。

然而这可能发生相当令人不快的后果。例如,假设有两个成对的虫洞 A(1,1)A(1,1) 和 B(3,1)B(3,1),Bessie 从 (2,1)(2,1) 开始朝着 xx 正方向移动。Bessie 将进入虫洞 B(3,1)B(3,1),从 A(1,1)A(1,1) 出去,然后再次进入 BB,困在一个无限循环中!

FJ 知道他的农场里每个虫洞的确切位置。他知道 Bessie 总是向 xx 正方向走进来,虽然他不记得贝茜的当前位置。

请帮助 FJ 计算有多少种虫洞配对方案,使得存在一个位置,使得 Bessie 从该位置出发,会被困在一个无限循环中。

输入格式

第一行一个正整数 nn,表示虫洞数量。

接下来 nn 行,每行两个整数 x,yx,y,表示一个虫洞的坐标。

输出格式

输出一行一个整数表示答案。

输入输出样例

输入 #1

4
0 0
1 0
1 1
0 1

输出 #1

2

说明/提示

数据范围

对于 100%100% 的数据,2≤n≤122≤n≤12,0≤x,y≤1090≤x,y≤109。
保证 nn 为偶数。

样例解释

将虫洞编号为 1∼41∼4,然后通过将 1,21,2 和 3,43,4 匹配,如果 Bessie 从 (0,0)(0,0) 到 (1,0)(1,0) 之间的任意位置出发,她会陷入无限循环中。

相似的,在相同的起始点,如果配对是 1,31,3 和 2,42,4,贝茜也会陷入循环。(如果贝西从 33 进去,11 出来,她会走向 22 ,然后被传送到 44,最后又回到 33)

仅有 1,41,4 和 2,32,3 的配对允许贝茜从任何二维平面上的点向 xx 正方向走,而不出现无限循环。

题面翻译摘自 NOCOW

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n;
struct node
{
    int x;int y;
} a[30];
int b[30];
int ans;
bool cmp(node aa,node bb)    //排序 
{
    if (aa.y==bb.y) return aa.x<bb.x;
    return aa.y<bb.y;
}
int f(int num,int d,int begin,int p1)    //检查,num只是用来标记的 
{                                    //begin,记录开始位置,用来判断是否回到原点了,
                                   //p1=1:走的方式到达点d 的,p1=0:从某虫洞到达d 的 
    if (num!=1&&d==begin&&p1==1) return 1;     // 回到原点了,而且是用走的方式 
    if (p1==0)    //从虫洞d出来,就往前走,如果前面有虫洞,就走过去,没有就返回0 
    {
        if (a[d].y==a[d+1].y) return f(num+1,d+1,begin,1);
        else return 0;    //没有形成环就返回0 
    }
    if (p1==1) return f(num+1,b[d],begin,0);    //走到虫洞口了就跳进去 
}
bool check()
{
    for (int j=1;j<=n;j++)    //尝试从每个点出发,看会不会形成环 
        if (f(1,j,j,1)==1) return 1;    //有一个会形成环就返回1 
    return 0;
}
void dfs(int x)    //配对
{
    if (x==n+1)    {if (check()==1) ans++;return;}    //n个点都匹配完了,就检查 
    if (b[x]==0)    //如果x没有匹配 
    {
        for (int i=x+1;i<=n;i++)    //一个点一个点的匹配 
            if (b[i]==0)
            {
                b[i]=x;b[x]=i;    //标记,i匹配x,x匹配i 
                dfs(x+1);    //往后搜 
                b[i]=0;b[x]=0;    //还原 
            }

    }
    if (b[x]!=0) dfs(x+1);    //x匹配过了就继续往后搜 
    return;
}
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
    {
        scanf("%d%d",&a[i].x,&a[i].y);
    }
    sort(a+1,a+n+1,cmp);    //排序按照纵坐标排,纵坐标相同按横坐标 
    dfs(1);     //一个点一个点的匹配 
    printf("%d\n",ans);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值