ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

CF1444D Rectangular Polyline[题解]

2021-02-21 14:32:56  阅读:213  来源: 互联网

标签:int 题解 线段 else ++ Polyline CF1444D s1 dp


构造

题目大意

给定 \(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;
}

标签:int,题解,线段,else,++,Polyline,CF1444D,s1,dp
来源: https://www.cnblogs.com/Defoliation-ldlh/p/14425712.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有