【题解】 Knots UVA - 1624
看到了一道不一样的题
一道关于“解结”的题
真的是没有思路,连题都看不懂
然后抄别人的代码,看别人的题解
大体框架是把输入的P个上下压在一起的数对给消去,最终变成一个简简单单的圈圈
别人的题解:
Ideas: 一个比较简单的想法是,我们模拟人解绳子的过程。具体怎么解呢?
- 如果我们发现,通过平移某一段绳子可以使交点个数减少,那么我们就这么操作。
- 如果我们发现,通过翻转某一段绳子可以使交点个数减少,那么我们就这么操作。
仔细想想,在人解绳子的过程中,貌似再没有任何其它高级的操作,因此我们可以认为以上两种操作可以解开任何可以解开的绳子。同时,题目中也说,任何一个合法绳环都可以这样生成。
对于第一个操作,我们可以这样判定:如果存在两段绳子 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;
}
不过我至少懂了,而且注释是我自己写的