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 ...
随机推荐
- SpringCloud Alibaba实战(2:电商系统业务分析)
选用了很常见的电商业务来进行SpringCloud Alibaba的实战. 当然,因为仅仅是为了学习SpringCloud Alibaba,所以对业务进行了大幅度简化,这里只取一个精简版的用户下单业务 ...
- 危险!水很深,让叔来 —— 谈谈命令查询权责分离模式(CQRS)
多年以前,那时我正年轻,做技术如鱼得水,甚至一度希望自己能当一辈子的一线程序员. 但是我又有两个小愿望想要达成:一个是想多挣点钱:另一个就是对项目的技术栈和架构选型能多有点主动权. 多挣点钱是因为当时 ...
- Docker学习(9) Docker守护进程的配置和操作
- Camera HDR Algorithms
Camera HDR Algorithms HDRI是High-Dynamic Range(HDR)image的缩写,也就是高动态范围图像.它就是为了解决更好的存储高动态范围图像这个问题而发明出来的. ...
- springboot——简单通过Map将错误提示输出到页面显示
主要思路:在controller层我们将错误信息put进map中,然后通过视图解析器跳转到目标页面,在目标页面中在通过指定标签内的th:text将错误消息取出. 例: 1.编写controller代码 ...
- vue3.0的变化
初涉vue3.0,下面是我在demo中遇到的一些问题(我是用的vue-cli进行开发) [1]main.js中配置 第一个变化 vue2.x === Vue.prototype.$baseURL= ...
- PyQt5开发实践(一、准备篇)
前言 近一年来我开发了不少PyQt小项目,因为之前没用过使用C++语言的Qt,所以可以算是从零基础开始边学边做的,这个过程中再一次体会到国内技术社区的匮乏-- 国内关于PyQt的资料说少不少,说多也不 ...
- Java面试指北!13个认证授权常见面试题/知识点总结!| JavaGuide
大家好,我是 Guide哥!端午已过,又要开始工作学习啦! 我发现有很多小伙伴对认证授权方面的知识不是特别了解,搞不清 Session 认证.JWT 以及 Cookie 这些概念. 所以,根据我根据日 ...
- 【Java】Debug断点调试常用技巧
Debug操作技巧 Show Execution Point 将光标回到当前断点停顿的地方 Step Over 执行当前行代码,并将运行进度跳转到下一行. Step Into 进入到当前代码行的方法内 ...
- JAVA设计模式(6:单例模式详解)
单例模式作为一种创建型模式,在日常开发中用处极广,我们先来看一一段代码: // 构造函数 protected Calendar(TimeZone var1, Locale var2) { this.l ...