【数学】2011集训队出题 跳跳棋
来源:互联网 发布:php接口验证 编辑:程序博客网 时间:2024/05/01 21:06
【2011集训队出题】 跳跳棋 by 何朴藩
【问题描述】
跳跳棋是在一条数轴上进行的。棋子只能摆在整点上。每个点不能摆超过一个棋子。
我们用跳跳棋来做一个简单的游戏:棋盘上有3颗棋子,分别在a,b,c这三个位置。我们要通过最少的跳动把他们的位置移动成x,y,z。(棋子是没有区别的)
跳动的规则很简单,任意选一颗棋子,对一颗中轴棋子跳动。跳动后两颗棋子距离不变。一次只允许跳过1颗棋子。
写一个程序,首先判断是否可以完成任务。如果可以,输出最少需要的跳动次数。
【题解】
我们把3个数排好序。设三个数从小到大是a, b, c
设:s1=b-a s2=c-b
那么b可以跳动到a左边,或者c右边。
如果s1<s2,那么a可以跳到bc中间
如果s1>s2,那么c可以跳到ab中间
也就是说,如果s1≠s2,那么一个局面有3种跳法。
如果s1=s2,那么只有2种跳法。
如果我们用图来表示状态之间的关系,就很容易发现,状态之间组成的联系实际上是二叉树组成的森林。
每一个s1=s2的状态都是一棵二叉树的根。
其余的每个状态,a或c往中间跳表示往父亲节点走一步
对于所有状态,中间节点往左右跳分别对应往左右孩子走一步。
原问题转换成了树上最短路问题。我们设起始状态对应节点p,目标状态对应节点q。
那么问题是:1.p和q是否同根。 2.如果同根,求p到q的距离。
这两个问题都可以用LCA的知识来解决。
如果p和q不存在LCA那么输出NO。
如果存在,那么计算LCA(p,q)到p和q分别的距离,相加即为答案。
这样子暴力做仍然会超时。
【优化】
我们发现在有些状态,比如说1 2 999999999时,往父亲走每次只能走一步。
这样等走到根就需要走999999996步。这也是为什么算法2超时的原因。
也就是说,答案可能非常大。
可以利用简单的数学运算解决一次走多步的问题。
这样一来,我们可以直接计算p和q在二叉树中的深度。
为了方便求LCA,我们首先把p和q深度调整到相同。h(x)表示x的深度。
不妨设h(p)≤h(q)。我们把q往上走h(q)-h(p)步。
求2个深度相同的点的LCA,我们可以采用二分答案的方法。
对于二分答案:LCA到p的距离mid,
如果p往上走mid和q往上走mid到达的点相同,
那么 答案≤mid
否则 答案>mid
如此一来,我们得到了一个O((logS)2)的算法。期望得分:100
#include<cstdio>#include<cstring>#include<algorithm>#define fo(i,a,b) for (int i = a;i <= b;i ++)using namespace std;int a[2][3],anc[2][3],depth[2],tmp[2][3];void Find(int x,int y,int z,int id,int step){int ia = y - x;int ib = z - y;if (ia < ib){int tmp = (ib-ia)/ia;if ((ib-ia) % ia != 0) tmp ++;Find(x+tmp*ia,y+tmp*ia,z,id,step+tmp);} elseif (ib < ia){int tmp = (ia-ib)/ib;if ((ia-ib) % ib != 0) tmp ++;Find(x,y-tmp*ib,z-tmp*ib,id,step+tmp);} else{depth[id] = step;anc[id][0] = x;anc[id][1] = y;anc[id][2] = z;return;}}void Adjust(int *a,int lim){if (!lim) return;int ia = a[1] - a[0];int ib = a[2] - a[1];int tmp;if (ia < ib){tmp = (ib-ia)/ia;if ((ib-ia) % ia != 0) tmp ++;tmp = min(lim,tmp);a[0] += tmp * ia;a[1] += tmp * ia;} else{tmp = (ia-ib)/ib;if ((ia-ib) % ib != 0) tmp ++;tmp = min(lim,tmp);a[1] -= tmp * ib;a[2] -= tmp * ib;}Adjust(a,lim-tmp);}bool Equal(int *a,int *b){fo(i,0,2)if (a[i] != b[i]) return 0;return 1;}int main(){freopen("leap.in","r",stdin);freopen("leap.out","w",stdout);fo(i,0,1) fo(j,0,2)scanf("%d",&a[i][j]);sort(a[0],a[0]+3);sort(a[1],a[1]+3);Find(a[0][0],a[0][1],a[0][2],0,0);Find(a[1][0],a[1][1],a[1][2],1,0);if (Equal(anc[0],anc[1])){if (depth[0] > depth[1]) Adjust(a[0],depth[0]-depth[1]);else Adjust(a[1],depth[1]-depth[0]);int Ans = abs(depth[1]-depth[0]);int L = 0, R = min(depth[0],depth[1]), ans;while (L <= R){int mid = (L + R) >> 1;fo(i,0,1) fo(j,0,2) tmp[i][j] = a[i][j];Adjust(tmp[0],mid);Adjust(tmp[1],mid);if (Equal(tmp[0],tmp[1])){ans = mid;R = mid - 1;} else L = mid + 1;}Ans += ans * 2;printf("YES\n%d\n",Ans);} else puts("NO");return 0;}
- 【数学】2011集训队出题 跳跳棋
- bzoj2144 【国家集训队2011】跳跳棋
- 【2011集训队出题】happiness
- 【2011集训队出题】happiness
- 【2011集训队出题】圈地计划
- 【2011集训队出题】数颜色
- 【集训队出题2011】大楼 题解
- 【2011集训队出题】Crash的数字表格
- 【2011集训队出题】Crash的数字表格
- 【2011集训队出题】【GDKOI2010】圈地计划
- JZOJsenior1935.【2011集训队出题】单选错位
- jzoj1916 [2011集训队出题] 飞飞侠 spfa
- jzoj1935 [2011集训队出题] 单选错位 概率水题
- jzoj 1935. 【2011集训队出题】单选错位
- JZOJ1900 【2010集训队出题】矩阵
- 【2011集训队出题】【BZOJ 2154】【JZOJ 1938】Crash的数字表格
- 【BZOJ 2154】【JZOJ 1938】【2011集训队出题】Crash的数字表格
- [JZOJ1919] [BZOJ2127]【2011集训队出题】happiness(最小割之二元关系)
- Ubuntu中Git服务器搭建
- IOS 第三方框架大全
- 分析电镀废水处理工艺特点
- Matlab的基本应用1
- Android编程学习(一)
- 【数学】2011集训队出题 跳跳棋
- cf——A. Unusual Product
- 使用拦截器完成权限控制
- 设置PL/SQL Developer 字符集
- c中全局变量,全局结构体使用
- apache问题之动与静态编译
- POI兼容XLSX和XLS
- pl/sql中文显示为乱码解决
- JsUnit 下载地址