兔子跳跃之谜

来源:互联网 发布:40x10÷1.25的简便算法 编辑:程序博客网 时间:2024/05/17 05:12

兔子跳跃之谜下

题目描述

小生和小森在玩兔子之谜游戏。有三只兔子排成一排。知道每只兔子的初始位置,以及三个兔窝的位置。 

游戏的规则是,重复以下步骤k次:选择两个不同的兔子AB,分别位于abA可以跳过B到达2*b-a的点: 

 

跳跃是不允许其他小兔子已经在点2*b-a的位置上: 

 

跳跃也不允许一次跳过一个以上的兔子: 

 

现在小生和小森想要知道,k次操作之后,能否让所有兔子都分别跳到一个兔窝里面。注意,第i个兔子并不一定要在第i个巢。并且输出跳法的种数,数值可能很大,要对结果取模1000000007。只要有一个跳跃是不同的,两种方式被认为是不同的。

 

输入

有多组测试数据: 
第一行,包含一个整数Num,表示测试数据的个数。(1<=Num<=10 
每组测试数据, 
第一行三个整数,第二行三个整数,分别表示兔子的初始位置和兔窝的位置。两组数值都是严格递增给出。范围均为[-10^18,10^18] 
最后一个整数k[1,100] 

输出

Num行, 
跳跃的种数。 

样例输入

8

0 5 8

0 8 11

1

0 5 8

0 8 11

3

0 5 8

0 8 11

2

5 8 58

13 22 64

58

0 1 2

1 2 3

100

5 8 58

20 26 61

58

67 281 2348

235 1394 3293

83

-1000000000000000000 999999999999999998 999999999999999999

-1000000000000000000 999999999999999999 1000000000000000000

5

样例输出

1

5

0

0

0

537851168

167142023

29

 

题解:

    首先考虑一下题目的特性

    显然,中间的兔子可以往两边跳跃,而外面的兔子只有一只能往里面跳跃,甚至不能跳跃(当三只兔子等距时),这种性质让我们想到了什么?没错,就是树,二叉树。

    一棵二叉树上的任意节点都有两个儿子结点和一个父亲结点(除了根和叶),而我们可以把三只兔子的坐标想象成树上的一个节点,每一次跳跃都是树上的一次相邻节点的移动,兔子们有无限的状态,所以这棵树也是无限的,甚至这是一片森林,因为有不同的等距状态。

有了这个思想的转化之后,我们就可以着手解决这个问题了。

求方案数,暴搜显然不可以,可以考虑树上DP,首先我们得判断一下这两只兔子在k步以内能否相遇。难道要把每只兔子k步能跳跃到的节点都暴搜出来在两两比较吗?显然不用。我们可以把每只兔子向上走k步以内的父亲结点求出来,再一一比较是否有相同的就行了(想一想为什么),这就是最暴力的做法。

我们用dp[i][j][k]表示初始节点ALCA  i步,结束点BLCA  j步,还剩下k步时的方案总数。为了方便处理,我们假设B节点不动(这很重要,也很巧妙)。

Case 1when i>0   ,此时我们可以让A向上或者向下走

Dp[i][j][k]=dp[i-1][j][k-1]+dp[i+1][j][k-1]*2

Case 2when i=0,j>0,此时我们也可以让A向上或者向下走,但是要注意A向上走时两者的LCAA,所以也要跟着动,要注意照状态写方程

Dp[0][j][k]=dp[0][j+1][k-1]+dp[0][j-1][k-1]+dp[1][j][k-1]

Case 3when i=0,j=0,种这情况类似于case2,同样要注意A向上走时两者的LCAA,所以也要跟着动,要注意照状态写方程

Dp[0][0][k]=dp[0][1][k-1]+dp[1][0][k-1]*2

到了这里,问题已经解决了大半,但还有一点需要非常重视!那就是根的情况。

case2case3中存在着向上走的情况,这是要注意A是否走到了根节点,因为到了根节点后会无法向上走。

来自lzw大神的想法,我们有了一种非常重要又简单的判断方法,注意到我们一开始做的时候假定了B节点不懂,而我们一开始又能够处理出来B离根节点的距离,而到根节点的情况只会在i=0A向上走,然后j+1是产生,所以我们断定j>disdp0disB到根节点的距离

另外一种解释是把状态解释为B向上j步为LCAA向上i步为LCA这样接可以轻易的看出j>disdp0

具体可以看代码。

#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>using namespace std;#define ll long long#define MOD 1000000007const int maxk=110;struct node{ll x,y,z;}fa[2][maxk];int f[maxk][maxk][maxk];int num,k,t1,t2,deep_a,deep_b;node a,b;bool flag;inline bool pd(node a,node b){return (a.x==b.x)&&(a.y==b.y)&&(a.z==b.z);}bool check(node a){if (a.y-a.x==a.z-a.y) return false;else return true;}void get_fa(node a,node &b){ll x=a.x,y=a.y,z=a.z;if (y-x<z-y){b.x=y; b.y=y+y-x; b.z=z;}else if (y-x>z-y){b.x=x; b.y=y+y-z; b.z=y;}}int dp(int i,int j,int k){if (k==0) if (i==0&&j==0) return 1;else return 0;if (i+j>k||j>deep_b) return 0;if (f[i][j][k]!=-1) return f[i][j][k];//printf("%d  %d  %d  %d  %d\n",i,j,k,deep_a,deep_b);if (i>0 && j>0){f[i][j][k]=(dp(i+1,j,k-1)*2LL+dp(i-1,j,k-1))%MOD;}else if (i==0&&j>0){f[i][j][k]=((ll)dp(1,j,k-1)+dp(0,j-1,k-1)+dp(0,j+1,k-1))%MOD;}else if (i>0 && j==0){f[i][j][k]=(dp(i+1,0,k-1)*2LL+dp(i-1,0,k-1))%MOD;}else if (i==0&&j==0){f[i][j][k]=(dp(1,0,k-1)*2LL+dp(0,1,k-1))%MOD;}return f[i][j][k];}int main(){scanf("%d",&num);while (num--){scanf("%lld%lld%lld",&a.x,&a.y,&a.z);scanf("%lld%lld%lld",&b.x,&b.y,&b.z);scanf("%d",&k);fa[0][0]=a; fa[1][0]=b;deep_a=0; deep_b=0;for (int i=1;i<=k&&check(fa[0][i-1]);i++){get_fa(fa[0][i-1],fa[0][i]);deep_a=i;}for (int i=1;i<=k&&check(fa[1][i-1]);i++){get_fa(fa[1][i-1],fa[1][i]);deep_b=i;}flag=true; t1=-1; t2=-1;for (int i=0;i<=deep_a&&flag;i++)for (int j=0;j<=deep_b&&flag;j++)if (pd(fa[0][i],fa[1][j])) { t1=i; t2=j; flag=false; }if (t1==-1) { printf("0\n"); continue; }f[0][0][0]=1;memset(f,-1,sizeof(f));printf("%d\n",dp(t1,t2,k));}return 0;}


原创粉丝点击