[USACO21DEC] Bracelet Crossings G

文章描述了一个编程竞赛题目,涉及到的手链问题。题目要求检查在特定条件下,多条手链是否仍满足不自交、不相互交叉的条件。给定手电筒扫描的结果,需要判断手链的状态。解决方案包括检查手链连续性、判断手链间关系(包含、相离或相交),并提供了代码实现。

洛谷[USACO21DEC] Bracelet Crossings G

题目大意

B B B n n n条手链,编号为 1 1 1 n n n,且第 i i i条手链的颜色为 i i i。这些手链满足以下三个条件:

  • 每个手链是一个简单闭合折线,并且第一个点和最后一个点相同
  • 没有手链与自身相交
  • 没有两条手链相交

在手链摆好后,Farmer John开着拖拉机经过,桌子晃动起来,导致手链四处移动甚至可能断成了多个折线,小 B B B还是想检查以上三个条件是否仍然成立。然而,天色已暗,她现在无法看清手链。

但小 B B B有手电筒,她选择了 m m m条垂线 x = 1 , x = 2 , … , x = m x=1,x=2,\dots,x=m x=1,x=2,,x=m,对于每条直线,她用手电筒从 y = − ∞ y=-\infty y=扫到 y = + ∞ y=+\infty y=+,按照出现的顺序记录她看到的所有手链的颜色。没有光束穿过任何折线的顶点或同时穿过两条线段。对于每一束光,所有出现的颜色都恰好出现了两次。

求现在这 n n n条手链是否可能满足上面的三个条件。

t t t组数据。

1 ≤ t ≤ 50 , 1 ≤ n ≤ 50 , 1 ≤ m ≤ 50 1\leq t\leq 50,1\leq n\leq 50,1\leq m\leq 50 1t50,1n50,1m50


题解

首先,检查每个手链是不是连续的,不连续的话直接输出 N O NO NO

再两两判断一下手链之间是否相交。对于手链 i i i和手链 j j j,我们可以判断手链是否有包含关系或相离关系。

包含比较好判断,直接看被包含的手链的颜色是否在包含的手链的颜色之间即可。

相离就不好直接判断了,我们可以分别判断。看 i i i是否完全在 j j j下面或 j j j是否完全在 i i i下面。如果 i i i j j j x x x轴的投影上有交集,则只要 i i i完全在 j j j下面或 j j j完全在 i i i下面,就是相离关系。如果 i i i j j j x x x轴的投影上没有交集,则显然不相交,这里也会判定其为相离。

如果既不是包含关系也不是相离关系,则就相交了。

总时间复杂度为 O ( ∑ n 2 m ) O(\sum n^2m) O(n2m)

code

#include<bits/stdc++.h>
using namespace std;
int t,n,m,lt[105],rt[105],c1[105][105],c2[105][105];
bool baohan(int i,int j){
	if(!(lt[i]<=lt[j]&&rt[i]>=rt[j])) return 0;
	for(int o=lt[j];o<=rt[j];o++){
		if(!(c1[o][i]<c1[o][j]&&c2[o][i]>c2[o][j])) return 0;
	}
	return 1;
}
bool xia(int i,int j){
	for(int o=1;o<=m;o++){
		if(c2[o][i]&&c1[o][j]&&c2[o][i]>c1[o][j]) return 0;
	}
	return 1;
}
bool pd(){
	for(int i=1;i<=n;i++){
		if(!lt[i]) continue;
		for(int j=lt[i];j<=rt[i];j++){
			if(!c1[j][i]) return 0;
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<i;j++){
			if(!baohan(i,j)&&!baohan(j,i)&&!xia(i,j)&&!xia(j,i)) return 0;
		}
	}
	return 1;
}
int main()
{
	scanf("%d",&t);
	while(t--){
		scanf("%d%d",&n,&m);
		memset(lt,0,sizeof(lt));
		memset(rt,0,sizeof(rt));
		memset(c1,0,sizeof(c1));
		memset(c2,0,sizeof(c2));
		for(int i=1,k;i<=m;i++){
			scanf("%d",&k);
			for(int j=1,x;j<=k;j++){
				scanf("%d",&x);
				if(!c1[i][x]) c1[i][x]=j;
				else c2[i][x]=j;
				if(!lt[x]) lt[x]=i;
				rt[x]=i;
			}
		}
		if(!pd()) printf("NO\n");
		else printf("YES\n");
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值