AtCoder AGC017 E题 Jigsaw 欧拉回路

EJigsaw

Problem Statement

给定 n 个物体,每个物体有 3 个横坐标,中间高度为 h,左右两边分别会有一部分突出(可能悬空,有高度)。
在这里插入图片描述

问是否存在一种摆放方案,使得摆成的效果,中间不能有缝隙,必须相互接触才行。下面这样的就是反例:
在这里插入图片描述

分析:
这道题目对我这样的CJ有好大的难度,唉~
我看了一些博主的博客才知道这是什么意思,要用什么思路去解答,奈何鄙人没做过欧拉回路的问题,都不知道还能用这个知识来解决题目。

首先要了解什么是欧拉回路,在这里我就不赘述了,直接用结论:
对于有向图来说,在连通的情况下,保证每个顶点的入度 = 出度即可

那么接下来问题来了,怎么建图呢,既然要用图论来解决,那么怎么建模就成了这题的关键。(不得不说,这个建图的思路我是看了好久才知道是怎么建的,tql)

我们规定两种点,一种称为负数点,简称N点;一种称为正数点,简称P点。
其中将边的长度量化为点,即每一个高度值代表一个点。

那么该怎么划分呢?规定当C=0时,归为N点集合,即下图的negtive2,当C!=0时,即为下图的positive1;同样的,当D=0时,归为P点集合,即为下图的positive2,反之为negtive1;(可以把这个问题想象成消消乐是不是好理解呢)。
在这里插入图片描述

要是可以根据题意那样相互连接,就要保证negtive1=negtive2才行。(到这步,可能有同学疑惑,下面这个图明显negtive1!=negtive2呀,确实没错,但这里的negtive1等于这个本身块的D的长度,看代码就好理解了)

  l_bound=c?P(c):N(a);//c>0,则l_bound=+c,反之为-a;这里的+-代表正负,不是加减
  r_bound=d?N(d):P(b);//d>0,则r_bound=-b,反之为+d;+-含义同上
  Merge(l_bound,r_bound);//之所以要用到并查集,是将可以相连的连接在一起,构成一个连通图

如下图,就可以将-1,1,3连成一个整体,这就说明了这两个模块是可以连成一个整体的,当然这些连线建模都是逻辑上的,要去理解才行。
[在这里插入图片描述](https://img-blog.csdnimg.cn/20210131132200606.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NfX19jMTg=,size_16,color_FFFFFF,t_70)

还有再说几处不太好理解的代码吧,比如下面这个

 for(int i=1;i<=h+h;i++)
        if(in[i] != out[i])
            vis[Findf(i)]=1;

这样就把-1,3这样的边界点去掉了
在这里插入图片描述

还有这个

for(int i=P(1);i<=P(h);i++)
        if(in[i] > out[i])
            return false;

这个说的是什么意思呢?是这个意思,根据题意,我们知道要保证不能有镂空的地方,但是呢,如果模块凸出的部分都不是悬空的,那么直接连接就好,反正连接之后不会出现镂空的现象。
比如这样
在这里插入图片描述
因此,只要保证同等高度下,出度大于等于入度就好,反之一定不能满足题意,直接输出NO,再举一个不满足的例子
在这里插入图片描述

上图就是out[1]=1>in[1]=0,显然不合适

同样上图也可以解释下面的代码
out[-1]=1 < in[-1]=2

  for(int i=N(1);i<=N(h);i++)
        if(out[i] < in[i])
            return false;
#include <iostream>
#include <cmath>
#include <algorithm>
#include <queue>
#include <vector>
#include <cmath>
#include <map>
#include <set>
#include <cstring>
#include <stack>
#include <string>
#define N(x) (x+h)
#define P(x) (x)
using namespace std;

typedef long long ll;
const int N = 1e5+199;
const double Pi = acos(-1);
int n,h;
int f[N],out[N],in[N];
int Findf(int x){
    if(f[x]==x) return x;
    else return f[x]=Findf(f[x]);
}
void Merge(int a,int b){
    int t1=Findf(a);
    int t2=Findf(b);
    if(t1!=t2){
        f[t2]=t1;
    }
}
bool vis[N];
bool check(){
    for(int i=P(1);i<=P(h);i++)
        if(in[i] < out[i])
            return false;
    for(int i=N(1);i<=N(h);i++)
        if(out[i] < in[i])
            return false;

    for(int i=1;i<=h+h;i++)
        if(in[i] != out[i])
            vis[Findf(i)]=1;
    for(int i=1;i<=h+h;i++)
        if(!vis[Findf(i)]&&(in[i] || out[i])) return false;
    return true;
}
int main()
{

    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif // ONLINE_JUDGE
    for(int i=0;i<=500;i++) f[i]=i;
    int l_bound,r_bound;
    int a,b,c,d;
    scanf("%d%d",&n,&h);
    for(int i=0;i<n;i++){
        scanf("%d%d%d%d",&a,&b,&c,&d);
        //注意这里要将负数变成正数0~h,正数变成h+1~2*h
        l_bound=c?P(c):N(a);//c>0,则l_bound=+c,反之为-a;
        r_bound=d?N(d):P(b);//d>0,则r_bound=-b,反之为+d
        Merge(l_bound,r_bound);
        //这里哪个是入度和出度没有关系,这里我令其为左边是出度,右边是入度
        //若要是改成相反的,check函数的前两个for循环也要改一下
        out[l_bound]++,in[r_bound]++;
    }

    if(check())
        printf("YES\n");
    else
        printf("NO\n");
    return 0;
}

总得来说,上面的代码解题思路好像是用排除法来做的,不知道我理解的对不对,所举得的样例都是我临时想到的,如果有不合适的地方,希望大家批评指出呀─=≡Σ(((つ•̀ω•́)つ

参考:https://www.cnblogs.com/yoyoball/p/9811158.html
https://mrsrz.github.io/AGC017E/

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值