https://www.acwing.com/problem/content/1354/
- 枚举匹配方法
- 判环
#include<bits/stdc++.h>
using namespace std;
const int N=15;
int n;
int to1[N],to2[N];//to1是向右移动的单向边,to2是虫洞的双向边
bool st[N],used[N][2],cur[N][2];
//used[i][0] 表示双向边的虫洞
//used[i][1] 表示水平的移动
struct node{
int x,y;}S[N];
bool cmp(node a,node b)
{
if(a.y==b.y) return a.x<b.x;
return a.y>b.y;
}
int ans;
bool dfs_c(int a,int b)
{
if(cur[a][b]) return true;
if(used[a][b]) return false;
cur[a][b]=used[a][b]=true;
bool res=false;
if(!b)//a的入点,要传送,也就是要走to2
{
if(dfs_c(to2[a],1)) res=true;
//走其传送到的出点(即水平走),因为是俩俩配对的,它已经配过对了,故只可以水平走
}
else//a的出点,要水平的走
{
if(to1[a]!=-1&&dfs_c(to1[a],0)) res=true;//水平的走,且有点
}
cur[a][b]=false;
return res;
}
bool check()
{
memset(used,0,sizeof used);
memset(cur,0,sizeof cur);
for(int i=0;i<n;i++)
for(int j=0;j<2;j++) if(dfs_c(i,j)) return true;
return false;
}
void dfs(int u)
{
if(u==n/2)
{
if(check()) ans++;
return;
}
for(int i=0;i<n;i++)
{
if(!st[i])
{
for(int j=i+1;j<n;j++)
{
if(!st[j])
{
st[i]=st[j]=true;
to2[i]=j,to2[j]=i;
dfs(u+1);
to2[i]=to2[j]=-1;
st[i]=st[j]=false;
}
}
break;//选完了可以退了,不然会多选
}
}
}
int main(void)
{
cin>>n;
for(int i=0;i<n;i++) cin>>S[i].x>>S[i].y;
sort(S,S+n,cmp);//排序,将同一水平线的点弄到一块
memset(to1,-1,sizeof to1);
memset(to2,-1,sizeof to2);
for(int i=1;i<n;i++)
if(S[i].y==S[i-1].y) to1[i-1]=i;//说明这俩是同一水平线的点
dfs(0);
cout<<ans<<endl;
return 0;
}