整数变换问题

整数变换问题

描述:

整数i的两种变换定义为[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k6dpn4EV-1639493352658)(file:///C:/Users/86152/AppData/Local/Temp/msohtmlclip1/01/clip_image002.gif)],[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZqZQHftH-1639493352661)(file:///C:/Users/86152/AppData/Local/Temp/msohtmlclip1/01/clip_image004.gif)](向下取整);设计一个算法求给定两个整数a和b,用最少次数的[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NRgxgR0v-1639493352661)(file:///C:/Users/86152/AppData/Local/Temp/msohtmlclip1/01/clip_image006.gif)]和[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F471zcv3-1639493352662)(file:///C:/Users/86152/AppData/Local/Temp/msohtmlclip1/01/clip_image008.gif)]变换将整数a变换为b;例如[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u5omNHGZ-1639493352662)(file:///C:/Users/86152/AppData/Local/Temp/msohtmlclip1/01/clip_image010.gif)]

实现提示:

观察f和g两个操作可知,f总是使得i变大,g总是使得i变小。因此在决定让x执行哪个操作之前可以先判断i和目标值m之间的大小关系。如果x>m,就让其执行g操作;反之,执行f操作。

问题的解分为两种情况
  • 一种是有解,即n可以通过函数变换成m
  • 一种是无解,即n无法通过函数变换成m

有解的情况比较容易,只需要判断最后的i是否等于m即可。如果i等于m,那么说明n已经被变换成m了,递归返回。

无解的情况可用下例分析。假设输入n=9,m=5。

n>m,执行g,n=[9/2]=4

n<m,执行f,n=3*4=12

n>m,执行g,n=[12/2]=6

n>m,执行f,n=[6/2]=3

n<m,执行g,n=3*3=9

n>m,执行f,n=[9/2]=4

如果n的值陷入了一个重复的循环,如果在递归的过程中,出现了前面计算过的元素,那就说明n是无法转换成m的。这种方法实现稍微复杂,需要判断当前所求出的数值之前是否出现过。 另一种简单的处理方式: 对于m无论如何变换都不能变为n的情况,可以加一个判断条件,比如深度达一个较大值为止(如1000)。

回溯法, 用子集树实现,子集树结构为:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Sozu3yhi-1639493352663)(file:///C:/Users/86152/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg)]

回溯返回条件有两个,一个是i等于m,另一个是出现了重复的数字。第二个返回条件可以用一个函数test来判断。

剪枝条件:

显式约束 : 如果x>m,就剪掉它的左子树;如果x<m,就剪掉它的右子树;

隐式约束:如果在某次计算的过程中发现当前的计算次数已经大于或等于最少计算次数了,那么就剪掉这个分支。

import java.util.Scanner;
public class Integer_transformation_problem {//整数变换问题,回溯
    static char []status = new char[1001];//记录变换路径
    static int times = 0;//记录路径的次数
    static int depth = 1;//记录深度

    public static int f(int i,int sum){
        if(i == 0) {//f函数
            return sum * 3;
        }
        else {//g函数
            return sum / 2;
        }
    }

    public static boolean backstrace(int n,int m,int currentDepth){
        int sum = n;
        if(currentDepth > depth) {//表明当前深度无法由 n --> m
            return false;
        }
        for(int i = 0;i <= 1;i++) {//0表示执行f函数,1表示执行g函数
            sum = f(i,n);
            if(sum == m || backstrace(sum,m,currentDepth + 1)) {
                switch(i) {
                    case 0:status[times++] = 'f';break;
                    case 1:status[times++] = 'g';break;
                }
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {//整数变换问题
        // TODO Auto-generated method stub
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int m = scanner.nextInt();
        while(!backstrace(n,m,1)){
            depth++;
            if(depth >= 1000){//当depth >= 1000;说明无法从 n --> m
                break;
            }
        }
        if(depth < 1000) {
            System.out.println("搜索深度为:"+depth);
            for(int i = 0;i < times;i++){
                System.out.print(status[i] + " ");
            }
            System.out.println();
        }
        scanner.close();
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
整数变换问题指的是,给定两个整数 a 和 b,允许执行以下两个操作之一: 1. 将 a 加上 b 的值; 2. 将 b 加上 a 的值。 求通过若干次这两个操作,能否将 a 变换成 b。 正确性证明如下: 首先,我们可以证明,对于任意两个正整数 a 和 b,它们能够通过若干次操作变换到一起,当且仅当它们的最大公约数能够整除它们的差值。 证明如下: 设 a 和 b 的最大公约数为 d,则 a = d * x,b = d * y,其中 x 和 y 互质。 假设我们通过若干次操作将 a 变换成 b,变换过程中,a 和 b 分别变换成了 a' 和 b'。则有: a' = a + k * b = d * x + k * d * y = d * (x + k * y) b' = b + m * a = d * y + m * d * x = d * (y + m * x) 其中 k 和 m 均为非负整数。 由于 x 和 y 互质,因此 x + ky 和 y + mx 也是互质的。因此,如果 a 和 b 能够通过若干次操作变换到一起,那么必然存在非负整数 k 和 m,使得 x + ky = y + mx。 将其变形得: (k - m) * y = (x - y) * m 由于 x 和 y 互质,因此 x - y 和 y 是互质的,因此必然存在 m 的逆元 m',使得 m' * (x - y) ≡ y (mod k - m)。 因此,我们可以取 k = m + m' * (x - y),得到一个满足条件的 k。此时,我们可以通过若干次操作将 a 变换成 b。 反之,如果 a 和 b 的最大公约数不能整除它们的差值,那么显然无法通过若干次操作将 a 变换成 b。 因此,我们可以通过检查 a 和 b 的最大公约数是否能够整除它们的差值,来判断它们能否通过若干次操作变换到一起。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值