1352. 虫洞【难度: 中 / 枚举 判环】

在这里插入图片描述
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;
}

おすすめ

転載: blog.csdn.net/qq_46527915/article/details/121268598