【题解】 Knots UVA - 1624

看到了一道不一样的题
一道关于“解结”的题
真的是没有思路,连题都看不懂
然后抄别人的代码,看别人的题解
大体框架是把输入的P个上下压在一起的数对给消去,最终变成一个简简单单的圈圈
别人的题解:

Ideas: 一个比较简单的想法是,我们模拟人解绳子的过程。具体怎么解呢?

  1. 如果我们发现,通过平移某一段绳子可以使交点个数减少,那么我们就这么操作。
  2. 如果我们发现,通过翻转某一段绳子可以使交点个数减少,那么我们就这么操作。

仔细想想,在人解绳子的过程中,貌似再没有任何其它高级的操作,因此我们可以认为以上两种操作可以解开任何可以解开的绳子。同时,题目中也说,任何一个合法绳环都可以这样生成。

对于第一个操作,我们可以这样判定:如果存在两段绳子 a,b
,a完全在 b的上方,并且两段绳子都从交点 A开始,到交点 B结束,不经过其它任何交点。那么我们可以直接删除这两个交点,因为通过平移 a,b

,这两个交点可以消失。

对于第二个操作,如果有一段绳子的左右端点重合于交点 A
,且不经过任何其它交点,我们可以删除交点 A,因为我们可以通过翻转这一段绳子使这个交点消失。

-end-

总结:平移和旋转

别人的题解2:

先用链表把每一个节点串起来,并对有覆盖的地方进行标记。

模拟解锁操作,如果一个节点和它所覆盖的节点之间没有其他结,那么进行逆self loop操作。

同理进行逆passing操作。

如果能把所有的结都解开,则答案是有解。

优化:把没有覆盖/被覆盖的节点提前删掉,缩短链表长度。

-end-

抄的别人的代码:


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <queue>

using namespace std;

struct node{
	int pre, nxt;
}a[1000010];
int link[1000010], ud[1000010], L, P;
void Del(int x){
	ud[x]=0;
	a[a[x].pre].nxt = a[x].nxt;
	a[a[x].nxt].pre = a[x].pre;
}
int T;
int main()
{
	cin >> T;
	int T0= T;
	while(T--){
		memset(link,0,sizeof(link));
		memset(ud,0,sizeof(ud));
		cin>>L>>P;
		for(int i=0;i<L;i++){
			a[i].pre = i-1;
			a[i].nxt = i+1;
		}
		a[0].pre = L-1;
		a[L-1].nxt = 0;
		int u, v;
		for(int i=1;i<=P;i++){
			cin>>u>>v;
			link[u]=v;link[v]=u;
			ud[u]=1;ud[v]=-1;//上 下
			 
		}
		for(int i=0;i<L;i++)
			if(!ud[i])	Del(i);
		int hd=0;
		while(P){//对每一个压迫的数对处理 
			bool flag=1;//不能删 
			while(!ud[hd])	hd++;
			for(int i=a[hd].nxt;i!=hd&&flag;i=a[i].nxt ){//每个数对都要处理一圈 
			//扫描一圈                  
				int u=i,v=a[i].nxt;//接下来我们的操作针对的是前后相连的两个点 
				
				if(ud[u]==ud[v] && (a[link[u]].nxt == link[v] || a[link[v]].nxt == link[u]))//???
				{//uv要在同一平面上    我(u)所压的那个点的前后相连的点是我旁边这个点v所压的点 ||  当我是v时如果可以的话
				//这就是传说中的平移 
					Del(u);Del(v);
					Del(link[u]);Del(link[v]);
					//删了这4个点 
					P-=2;//减少2对 
					flag=0;//能删了 
				}//passing
				
				else if(link[v]==u || link[u]==v){
				//上下遮挡 
				//这就是传说中的旋转 
					Del(u);Del(v);
					// 删了这两个点 
					P--;//减少1对 
					flag=0;//能删了 
				}//self loop
				
			}
			
			if(flag)	break;//不行 
		}
		printf("Case #%d: ",T-T0);
		if(!P)	printf("YES\n");//所有的压迫都被打开了 
		else printf("NO\n");
	}
	return 0;
}


不过我至少懂了,而且注释是我自己写的

posted @ 2020-05-17 17:54  _Buffett  阅读(210)  评论(0编辑  收藏  举报