CF1444D Rectangular Polyline[题解]
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[题解]的更多相关文章
- [cf1444D]Rectangular Polyline
由于两种线段要交替出现,有解的必要条件即为$h=v$(以下均记为$n$) 进一步的,再假设两种线段依次对应于向量$(a_{i},0)$和$(0,b_{i})$,根据题意要求向量长度为给定值且和为0,那 ...
- UVALive 3959 Rectangular Polygons (排序贪心)
Rectangular Polygons 题目链接: http://acm.hust.edu.cn/vjudge/contest/129733#problem/G Description In thi ...
- Codeforces Round #340 (Div. 2) D. Polyline 水题
D. Polyline 题目连接: http://www.codeforces.com/contest/617/problem/D Descriptionww.co There are three p ...
- 《ACM国际大学生程序设计竞赛题解Ⅰ》——基础编程题
这个专栏开始介绍一些<ACM国际大学生程序设计竞赛题解>上的竞赛题目,读者可以配合zju/poj/uva的在线测评系统提交代码(今天zoj貌似崩了). 其实看书名也能看出来这本书的思路,就 ...
- 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 ...
- USACO 6.1 A Rectangular Barn
A Rectangular Barn Mircea Pasoi -- 2003 Ever the capitalist, Farmer John wants to extend his milking ...
- 【模拟赛·polyline】
Input file: polyline.in Output file: polyline.out Time limit: 1s Memory limit: 128M 有若⼲个类似于下⾯的函数: 定义 ...
- CSU-2116 Polyline Simplification
CSU-2116 Polyline Simplification Description Mapping applications often represent the boundaries of ...
- Codeforces Round #320 (Div. 2) [Bayan Thanks-Round] C C Problem about Polyline 数学
C. A Problem about Polyline ...
随机推荐
- uwsgi启动Django应用
uwsgi启动Django应用 uWSGI是一个Web服务器,它实现了WSGI协议.uwsgi.http等协议. WSGI / uwsgi / uWSGI 三者区别: WSGI是一种通信协议,Fl ...
- Django学习之完成数据库主从复制、读写分离和一主多从情况下的使用办法
1.首先配置多个数据库,在settings配置文件中配置以下内容: DATABASES = { 'default': { #默认数据库,配置多个mysql数据也是ok的,混用数据库也是ok的 'ENG ...
- 激光雷达数据到云cloud
激光雷达数据到云cloud 在美国地质调查局的3D提升计划(3DEP)被激发到一个新的方式可用性宣布从3DEP仓库的访问和处理激光雷达点云数据. 3DEP一直在美国使用光检测和测距(激光)技术获取三维 ...
- TensorRT PoolingLayer
TensorRT PoolingLayer IPoolingLayer在通道中实现池.支持的池类型有maximum.average和maximum average混合. Layer Descripti ...
- kali2020.4安装openvas(gvm)
记录一下,方便复习 1.更换kali源,默认源注释掉. sudo vim /etc/apt/sources.list 中科大 deb http://mirrors.ustc.edu.cn/kali k ...
- JDBCTemplate基本使用
用了jdbc连接池之后,我们会发现连接对象的复用性更高了,程序整体运行的性能也更高了.但是我们在做JDBC操作的时候还是比较麻烦,要定义sql,执行sql,设置参数,处理结果. 特别是当我们要做查询操 ...
- ES7扩展
前一段时间小编一直在更新javascript es6版本的部分新语法和新特性,鉴于现在js一直在更新,接下来小编将和大家一起进步,一块探究js的新特性.今天小编就和大家一起来看看es7更新的语法和新特 ...
- Unix、Linux 软件包管理快速入门对照:apt、brew、pkg、yum
请访问原文链接:https://sysin.org/blog/apt-brew-pkg-yum/,查看最新版.原创作品,转载请保留出处. 作者:gc(at)sysin.org,主页:www.sysin ...
- Redis之复制
1.配置 1.1 建立复制 参与复制的Redis实例划分为主节点(master) 和从节点(slave).默认情况下,Redis都是主节点.每个从节点只能有一个主节点,而主节点可以同时具有多个从节点. ...
- centos 8.3安装 OPENJDK
centos 8.3安装 OPENJDK 查找可安装的OPENJDK [root@xamppr10 ~]# yum search java | grep -i --color openjdk 命令执行 ...