Rectangular Polyline

题目大意

给定 \(h\) 条长度分别为 \(l_1,l_2,……,l_h\) 的水平线段以及 \(v\) 条长度分别为 \(p_1,p_2,…….p_v\) 的竖直线段,将这些线段水平竖直交替首尾相连组成一个多边形,满足任意两条线段不相交或交点为各自的端点。可能无解

题意分析

是否有解

首先考虑有解的必要条件。

由于题目要求线段要交替摆放,所以线段的数量肯定满足 \(h=v\) ,否则无解。

其次,由于最后要首尾相连,设集合 \(X\) 为水平线段的集合,集合 \(Y\) 为竖直线段的集合,再设 \(X+ \subsetneqq X\) , \(X- \subsetneqq X\) , \(Y+ \subsetneqq Y\) , \(Y- \subsetneqq Y\) 。

假设我们从 \((0,0)\) 开始摆放线段,则线段的摆放是有方向的,设 \(X+\) 表示水平线段摆放的方向为正, \(X-\) 表示, \(Y+\) 与 \(Y-\) 同理。

设 \(X+\) 中线段的长度和为 \(sumx+\) , \(X-\) 中线段的数量为 \(sumx-\) 。则要做到首尾相连,则必有 \(sumx+=sumx-\) 。且设 \(Y+\) 中线段的长度和为 \(sumy+\) , \(Y-\) 中线段的数量为 \(sumy-\) 。则要做到首尾相连,则必有 \(sumy+=sumy-\) 。

证明易得,下不赘述。

那么,我们的问题在于,这些条件是否充分?即满足这些条件,是否一定有解。

那么答案就是它确实是充分的,下给出证明。

如何放置

假设 \(|X+|\leq |Y+|\) ,则我们的构造可以分成三段,第一段是 \(X+\) 与 \(Y+\) ,第二段是 \(X-\) 与 \(Y+\) ,第三段是 \(X-\) 与 \(Y-\) 。

则我们最后得到的图像大致如下图中的黑色三角形,且我们希望这三段的线段被框在红色线段与黑色三角形的边框形成的黄色范围内:

对于第一个子问题,我们想让形成的图像尽可能靠右下,则我们可以让 \(X+\) 从大到小排序,让 \(Y-\) 从小到大排序。

这样做为什么正确呢?

如图,第一阶段我们行走的路径将类似于绿色线段,设到达点的坐标为 \((sumx1,sumy1)\) ,设我们现在走到第一阶段中的第 \(k\) 步,且一共有 \(n\) 步,由于我们取的 \(x\) 是前 \(k\) 大,则我们目前的 \(sumxnow \ge \frac {k}{n}sumx1\) ,同理,由于我们取的 \(y\) 是前 \(k\) 小,则 \(sumynow \leq \frac {k}{n}sumy1\) ,所以 \(\frac {sumynow}{sumxnow} \leq \frac {sumy1}{sumx1}\) ,即当前的斜率至少比红线的斜率小,所以是一定处于红线下方的。

而对于另外的两段我们可以按照同样的方法进行构造

如下图,我们最终可以发现,\(X+\) 与 \(X-\) 都应该从大到小排序, \(Y+\) 与 \(Y-\) 都应该从小到大排序,这样就能够满足不相交的性质。

注意我们这样做的前提, \(|X+|\leq|Y+|\) ,如果情况恰好相反,则我们可以调换 \(X\) 与 \(Y\) ,到最后输出的时候再反转输出即可。

\(X+,X-,Y+,Y-\) 如何求得?

我们可以使用 \(bitset\) ,建立数组 \(dp[i]\) ,表示前 \(i\) 条线段中取任意线段长度相加的所有情况。

\(dp[i]\) 中第 \(j\) 为 \(0\) 则表示目前不能取到该值,为 \(1\) 则表示可以取到该值。

如何转移?

\(dp[i]=dp[i-1]|(dp[i-1]<<a[i])\) ,解读这个式子其实就是将 \(dp[i-1]\) 中每个 \(1\) 向右移动 \(a[i]\) 位,其实含义是加上了 \(a[i]\) ,在和原来的状态或运算,即可得到现在的状态。

具体代码实现如下:

inline void initial()
{
flag=true;
//先解决x数组
int s1=sum1/2,s2=sum2/2;
for(register int i=1;i<=n;i++) dp[i]=dp[i-1]|(dp[i-1]<<a[i]);
if(dp[n][s1]){ //有可能到达sum1/2
for(register int i=n;i>=1;i--){
//该判断的含义是看若取了这个数,上一个状态是否能够皆这取到剩下需要取的长度,若能,才可以取
if(s1>=a[i]&&dp[i-1][s1-a[i]]) s1-=a[i],x[1][++totx[1]]=a[i];
else x[0][++totx[0]]=a[i];
}
}
else { flag=false; return; } //再解决y数组
for(register int i=1;i<=m;i++) dp[i]=dp[i-1]|(dp[i-1]<<b[i]);
if(dp[m][s2]){ //有可能到达sum1/2
for(register int i=m;i>=1;i--){
if(s2>=b[i]&&dp[i-1][s2-b[i]]) s2-=b[i],y[1][++toty[1]]=b[i];
else y[0][++toty[0]]=b[i];
}
}
else { flag=false; return; }
}

结语

到这儿这道题就结束了,个人认为这是一道非常不错的好题,没有任何高深的算法,但是还是非常难,几乎和这道题死磕了半天。

CODE

#include <bits/stdc++.h>
using namespace std;
const int N=1e3+10;
struct node{ int x,y; };
int T,n,m,sum1,sum2;
int a[N],b[N];
//x[0][i]表示水平直线i被分在x-组中,记录直线i的长度
//x[1][i]表示水平直线i被分在x+组中,记录直线i的长度
//y[0][i]表示竖直直线i被分在y-组中,记录直线i的长度
//y[1][i]表示竖直直线i被分在y+组中,记录直线i的长度
int totx[2],toty[2];
int x[2][N],y[2][N];
int tot; node ans[N];
inline int read()
{
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
bitset<N*N> dp[N];
bool flag=true;
inline void initial()
{
flag=true;
//先解决x数组
int s1=sum1/2,s2=sum2/2;
for(register int i=1;i<=n;i++) dp[i]=dp[i-1]|(dp[i-1]<<a[i]);
if(dp[n][s1]){ //有可能到达sum1/2
for(register int i=n;i>=1;i--){
if(s1>=a[i]&&dp[i-1][s1-a[i]]) s1-=a[i],x[1][++totx[1]]=a[i];
else x[0][++totx[0]]=a[i];
}
}
else { flag=false; return; } //再解决y数组
for(register int i=1;i<=m;i++) dp[i]=dp[i-1]|(dp[i-1]<<b[i]);
if(dp[m][s2]){ //有可能到达sum1/2
for(register int i=m;i>=1;i--){
if(s2>=b[i]&&dp[i-1][s2-b[i]]) s2-=b[i],y[1][++toty[1]]=b[i];
else y[0][++toty[0]]=b[i];
}
}
else { flag=false; return; }
}
inline bool cmp(int p,int q) { return p>q; }
inline void sol()
{
bool Judge=false;
if(totx[1]<=toty[1]){
//x从大到小,y从小到大排序
sort(y[1]+1,y[1]+toty[1]+1);
sort(y[0]+1,y[0]+toty[0]+1);
sort(x[1]+1,x[1]+totx[1]+1,cmp);
sort(x[0]+1,x[0]+totx[0]+1,cmp);
node now={0,0};
int i[2]={1,1},j[2]={1,1};
while(n--){
if(i[1]<=totx[1]) now.x+=x[1][i[1]++],ans[++tot]=now;
else now.x-=x[0][i[0]++],ans[++tot]=now;
if(j[1]<=toty[1]) now.y+=y[1][j[1]++],ans[++tot]=now;
else now.y-=y[0][j[0]++],ans[++tot]=now;
}
}
else{
//反过来,y从大到小,x从小到大排序
sort(x[1]+1,x[1]+totx[1]+1);
sort(x[0]+1,x[0]+totx[0]+1);
sort(y[1]+1,y[1]+toty[1]+1,cmp);
sort(y[0]+1,y[0]+toty[0]+1,cmp);
node now={0,0};
int i[2]={1,1},j[2]={1,1};
while(n--){
if(j[1]<=toty[1]) now.y+=y[1][j[1]++],ans[++tot]=now;
else now.y-=y[0][j[0]++],ans[++tot]=now;
if(i[1]<=totx[1]) now.x+=x[1][i[1]++],ans[++tot]=now;
else now.x-=x[0][i[0]++],ans[++tot]=now;
}
}
}
int main()
{
T=read();
dp[0][0]=1;
while(T--){
totx[1]=totx[0]=toty[1]=toty[0]=0;
tot=0;
sum1=0,sum2=0;
n=read();
for(register int i=1;i<=n;i++) a[i]=read(),sum1+=a[i];
m=read();
for(register int i=1;i<=m;i++) b[i]=read(),sum2+=b[i];
if(n!=m||sum1&1||sum2&1) { printf("No\n"); continue; }
initial(); //预处理出x,y数组
if(!flag) { printf("No\n"); continue; }
sol();
printf("Yes\n");
for(register int i=1;i<=tot;i++) printf("%d %d\n",ans[i].x,ans[i].y); //输出方案
}
return 0;
}

CF1444D Rectangular Polyline[题解]的更多相关文章

  1. [cf1444D]Rectangular Polyline

    由于两种线段要交替出现,有解的必要条件即为$h=v$(以下均记为$n$) 进一步的,再假设两种线段依次对应于向量$(a_{i},0)$和$(0,b_{i})$,根据题意要求向量长度为给定值且和为0,那 ...

  2. UVALive 3959 Rectangular Polygons (排序贪心)

    Rectangular Polygons 题目链接: http://acm.hust.edu.cn/vjudge/contest/129733#problem/G Description In thi ...

  3. Codeforces Round #340 (Div. 2) D. Polyline 水题

    D. Polyline 题目连接: http://www.codeforces.com/contest/617/problem/D Descriptionww.co There are three p ...

  4. 《ACM国际大学生程序设计竞赛题解Ⅰ》——基础编程题

    这个专栏开始介绍一些<ACM国际大学生程序设计竞赛题解>上的竞赛题目,读者可以配合zju/poj/uva的在线测评系统提交代码(今天zoj貌似崩了). 其实看书名也能看出来这本书的思路,就 ...

  5. Codeforces Round #257 (Div. 1)A~C(DIV.2-C~E)题解

    今天老师(orz sansirowaltz)让我们做了很久之前的一场Codeforces Round #257 (Div. 1),这里给出A~C的题解,对应DIV2的C~E. A.Jzzhu and ...

  6. USACO 6.1 A Rectangular Barn

    A Rectangular Barn Mircea Pasoi -- 2003 Ever the capitalist, Farmer John wants to extend his milking ...

  7. 【模拟赛·polyline】

    Input file: polyline.in Output file: polyline.out Time limit: 1s Memory limit: 128M 有若⼲个类似于下⾯的函数: 定义 ...

  8. CSU-2116 Polyline Simplification

    CSU-2116 Polyline Simplification Description Mapping applications often represent the boundaries of ...

  9. Codeforces Round #320 (Div. 2) [Bayan Thanks-Round] C C Problem about Polyline 数学

                                                                           C. A Problem about Polyline   ...

随机推荐

  1. ZooKeeper学习笔记一:集群搭建

    作者:Grey 原文地址:ZooKeeper学习笔记一:集群搭建 说明 单机版的zk安装和运行参考:https://zookeeper.apache.org/doc/r3.6.3/zookeeperS ...

  2. [leetcode] 35. 搜索插入位置(Java)(二分)

    35. 搜索插入位置 二分,太简单,没啥好说的 class Solution { public int searchInsert(int[] nums, int target) { if (nums. ...

  3. 在Docker中安装MongoDB

    在Docker中安装MongoDB docker run -p 27017:27017 -v /data/mongodb:/data/db --name mongodb -d mongo --auth ...

  4. Python+Selenium - Alert弹框

    上面三种弹窗可以在浏览器的控制台做出效果,如下图 上面三种弹窗可以用alert方法处理 示例: #出现弹窗的操作xxxx# 切换al = driver.switch_to.alert# print(a ...

  5. Jmeter- 笔记5 - 从响应数据提取信息

    JSON提取器 提取 响应体(response body)里的信息 在需要提取数据的请求下添加 JSON提取器,一个JSON提取器可以写多个json提取器 路径:后置处理器 -> JSON提取器 ...

  6. 201871030129-魏琦 实验三 结对项目—《D{0-1}KP 实例数据集算法实验平台》项目报告

    项目 内容 课程班级博客链接 班级博客 这个作业要求链接 作业链接 我的课程学习目标 (1)掌握Github协作开发程序的操作方法:(2)理解并掌握代码风格规范.代码设计规范.代码复审.结对编程概念: ...

  7. arm,asic,dsp,fpga,mcu,soc各自的特点

    arm,asic,dsp,fpga,mcu,soc各自的特点 人工智能受到越来越多的关注,许多公司正在积极开发能实现移动端人工智能的硬件,尤其是能够结合未来的物联网应用,对于移动端人工智能硬件的实现方 ...

  8. 激光雷达Lidar Architecture and Lidar Design(下)

    Considerations on Lidar Design 双基地还是单基地? 双轴还是同轴? 几何重叠 向上还是向下看? 关心分散还是只关心时间? 发射器和接收器的波长 是否可调? 发射器和接收器 ...

  9. 开发平台支持Arm Cortex-M的微控制器上人工智能训练

    开发平台支持Arm Cortex-M的微控制器上人工智能训练 Development platform enables AI training on Arm Cortex-M-based microc ...

  10. Spring Bean 的生命周期总结

    除了使用阶段外,Spring 将 bean 的生命周期定义为实例化.属性填充.初始化和销毁四个阶段,并为每个阶段提供了多个拓展点用于自定义 bean 的创建过程.这篇文章介绍了 bean 的生命周期和 ...