Robot Arms(AtCoder-4432)(二进制构造,数学归纳证明,贪心策略)

前言

一道极为好的题,这思路我打包票比赛时写不出来…

题目

传送门:
Vjudge
Atcoder
题目大意:
现在你有n个点,然后让你构造一个长度为m的序列,序列中 d [ i ] d[i] d[i]是每一次移动的距离,输出这个序列,你对每个点对( x i , y i x_i,y_i xi,yi)每一次从原点出发执行m此操作,对于每一个指令你可以上下左右任选一个方向走 d [ i ] d[i] d[i]长度,执行完命令后恰好落到( x i , y i x_i,y_i xi,yi),输出对于每一点对的操作方式(‘U’,‘D’,‘L’,‘R’)
数据范围:
1 &lt; = n &lt; = 1000 1&lt;=n&lt;=1000 1<=n<=1000
1 &lt; = m &lt; = 40 1&lt;=m&lt;=40 1<=m<=40
− 1 0 9 &lt; = x i , y i &lt; = 1 0 9 -10^9&lt;=x_i,y_i&lt;=10^9 109<=xi,yi<=109

思路

构造长度

我们可以由我们的‘常识’和联想可以知道,二进制解决这种题一直都十分有优势,所以我们可以尝试从二进制构造入手,我们就让边长集合 d ∈ { 1 , 2 , 4 , 8 , . . . , 2 k } d∈\{1,2,4,8,...,2^k\} d{1,2,4,8,...,2k}我们发现
计算机
1 0 9 10^9 109只有最多只有29位,那是不是意味着我们k只用29就够了呢?其实我们的k要30才行,为什么呢?因为我们有 x i , y i x_i,y_i xi,yi两个坐标约束,但其实你开30以上也没有问题…呵呵呵,后面证明了一大堆,自己往下看吧.

注意,到了这里,过程思路要综合看一看…

奇偶性判断无解

我们发现,我们所构造的线段中只有1为奇数,剩下的 { 1 , 2 , 4 , 8 , . . . , 2 k } \{1,2,4,8,...,2^k\} {1,2,4,8,...,2k}都是偶数,那么我们对于一个坐标( x i , y i x_i,y_i xi,yi)而言( x i − d , y i x_i-d,y_i xid,yi)( x i + d , y i x_i+d,y_i xi+d,yi)( x i , y i − d x_i,y_i-d xi,yid)( x i , y i + d x_i,y_i+d xi,yi+d)当 d ≠ 1 d≠1 d̸=1是不会改变x+y的奇偶性的,那么我们如果走了1这条边,由于起点(0,0),0+0=0为偶数,所以下一个点变为奇数,为我们只走一次1,剩下只走偶数,所以之后x+y奇偶性不变,恒为奇数
那如果要走x+y为偶数的点呢?我们有两种方法思考

把1去掉

我们发现,我们仅仅是满足了x+y为偶数,但x,y仍奇偶性单独不确定,例如去掉后(1,1)就走不到了,所以这种方法不行
我们还可以想一下,就算所有x,y均为偶数,那么实际上我们又可以把整个坐标系缩小2倍,这又等价于我们要解决的问题,就是个极为难以实现的递归问题了

加一个1

这将原来x+y和为奇数前提上又转化为偶数,显然是可以的
那如果又有x+y为奇数,又有x+y为偶数的点显然就无解了

证明

能用边长集合 d ∈ { 1 , 2 , 4 , 8 , . . . , 2 k } d∈\{1,2,4,8,...,2^k\} d{1,2,4,8,...,2k}走到 ∣ x ∣ + ∣ y ∣ &lt; = 2 k + 1 − 1 |x|+|y|&lt;=2^{k+1}-1 x+y<=2k+11范围内所有的x+y为奇数的点。
用一幅图就是这样:
在这里插入图片描述
我们用数学归纳法证明:
令d(k)表示采用边集合 d ∈ { 1 , 2 , 4 , 8 , . . . , 2 k } d∈\{1,2,4,8,...,2^k\} d{1,2,4,8,...,2k}所能走到的范围
k = 0 k=0 k=0时,显然成立
k &gt; 0 k&gt;0 k>0时,我们可以用一用下面这一幅图,中间的小的正方形表示d(k-1),外面大的正方形表示d(k)
在这里插入图片描述
我们把小正方形切割成四块,我们关注内部最靠上的小正方形即可以将它向左、上、右,四个方向平移现在边的长度 2 k 2^k 2k得到下面的图形:在这里插入图片描述
那么对于剩下三个也这么操作后 d ( k ) − d ( k − 1 ) d(k)-d(k-1) d(k)d(k1)的范围我们都可以走到了
但是我们还不知道 d ( k − 1 ) d(k-1) d(k1)能否依旧走的到
我们可以假设落到红色正方形中,我们之前就可以先到d(k-1)中下部的小正方形,然后再向上走 2 k 2^k 2k即可
那么对于一个k而言,d(k)里所有为x+y为奇数的点都可以走到了
如果是偶数你多的一条长度为1的边,随便上下左右动一动就好了…

贪心走法

这个就跟LCA深度相同后找祖先差不多…
走法:将所有边集合 d ∈ { 1 , 2 , 4 , 8 , . . . , 2 k } d∈\{1,2,4,8,...,2^k\} d{1,2,4,8,...,2k}降序排序,每次对于一点( x , y x,y x,y),让x,y绝对值较大的一个用此时便 2 k 2^k 2k使它尽量变小(加或减)后总能到(0,0)
说得有点绕,看下面吧:
感觉跟上面的差不多…但这是倒着来的

令d(k)表示采用边集合 d ∈ { 1 , 2 , 4 , 8 , . . . , 2 k } d∈\{1,2,4,8,...,2^k\} d{1,2,4,8,...,2k}所能走到的范围,(x,y)为此时的点坐标

我们首先要知道, m i n ( ∣ x ∣ , ∣ y ∣ ) &lt; = 2 k − 1 min(|x|,|y|)&lt;=2^k-1 min(x,y)<=2k1这点很重要

k = 0 k=0 k=0时,显然成立
k &gt; 0 k&gt;0 k>0时,分两种情况
①当(x,y)为d(k-1)外部时:
在这里插入图片描述
注意,现在我们移动距离是大正方形对角线长的四分之一的 2 k 2^k 2k,不要搞混了
显然,往小正方形里面移动就是了,因为 m i n ( ∣ x ∣ , ∣ y ∣ ) &lt; = 2 k − 1 min(|x|,|y|)&lt;=2^k-1 min(x,y)<=2k1,把坐标中绝对值较大的绝对值变小就可以了
②当(x,y)为d(k-1)内部时:在这里插入图片描述
还是直接让绝对值较大的变小就是了
如果都为偶数多的长度为1的边,在d(0)时也是显然可以的…
那这就说完了。。。

代码

#include<set>
#include<map>
#include<ctime>
#include<queue>
#include<cmath>
#include<climits>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
int read(){
    int f=1,x=0;char s=getchar();   
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}  
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    return x*f;
}
#define MAXN 1000
#define INF 0x3f3f3f3f
int n,len,x[MAXN+5],y[MAXN+5],d[35];
void Print(int X,int Y){//贪心策略
	for(int i=1;i<=len;i++){
		if(abs(X)>abs(Y)){
			if(X>0) putchar('R'),X-=d[i];
			else putchar('L'),X+=d[i];
		}
		else{
			if(Y>0) putchar('U'),Y-=d[i];
			else putchar('D'),Y+=d[i];
		}
	}
	puts("");
	return ;
}
int main(){
	bool f[2]={0};
	n=read();
	for(int i=1;i<=n;i++)
		x[i]=read(),y[i]=read(),f[(x[i]+y[i])%2]=1;
	if(f[0]&&f[1]){//判断奇偶有无解
		puts("-1");
		return 0;
	}
	for(int i=30;i+1;i--)
		d[++len]=(1<<i);
	if(f[0]) d[++len]=1;//奇偶处理
	printf("%d\n",len);
	for(int i=1;i<len;i++)
		printf("%d ",d[i]);
	printf("%d\n",d[len]);
	for(int i=1;i<=n;i++)
		Print(x[i],y[i]);
	return 0;
}

题外话

如果你还不懂,或者不习惯作者的语言(hhh),你还可以看下面这一篇网上路人甲写的…(觉得他并没我写得好…)
有关构造证明
请用你的手指轻轻地点个赞~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值