切割绳子问题(附 java 代码)

本文介绍了一种使用二分查找法解决切割绳子问题的方法,通过设定合理的搜索区间,逐步逼近最优解,最终找出能从N条绳子中切割出K条相同长度绳子的最大长度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题:

N条绳子,它们的长度分别Li,i=1,⋯,N。如果从它们中切割出K条长度相同的绳子,这K条绳子每条最长能有多长?(我们这里考虑每条绳子的长度都是整数,切最后切割出来的绳子的长度也是整数。如果遇到的问题绳子长度不是整数类型,可将绳子的长度先扩大10n 倍,转换成整数形式 )

解题思路:

这个题需要用二分查找法来解决,为了方便讲解,我们假设绳子有4条,长度分别是{802,743,457,539},需要切割出11条长度相同的绳子(这个数据来自PTA 切割绳子问题

  1. 就我的理解,既然是用二分查找,那么就需要设置一个较为合适的区间[left right],将答案包括在这个区间里,那么我们就要先去把这样一个区间找出来。

//这下面的操作就是去寻找出这个区间
int left = 1;//理论上切割出绳子的最小值
int right = 10000;//理论上切割出绳子的最大值
for (int i = 0; i < n; i++) {
    ints[i] = scanner.nextInt();//读取每绳子的长度,并存入ints 数组内
    if (ints[i] < right){
        right = ints[i];//将最短的那条绳子的长度赋给 right
    }
}
//去调整left 和 right 的值,找到包含最优解的一个较为合适的区间
int res = 0;//res 表示切割出绳子的数量
for (int i = 0; i < n; i++) {//这个循环是去求当切割长度为right时,切割出来的绳子数量
    res += ints[i] / right;
}
while (res>=k){//这个循环是为了使得当切割长度为right时,切割出来的绳子数量小于所需要的绳子数量
    res = 0;
    int temp = right;
    right = right*2;
    left = temp;
    for (int i = 0; i < n; i++) {
        res += ints[i] / right;
    }
}
  1. 在找到一个较为合适的区间[left right]后,就利用二分查找法去求出一个较优的解

    //寻找一个较优值
    int mid = 0;//区间的中间值
    while (res != k){//二分查找经典做法,根据条件去缩小区间
        res = 0;
        mid = (right-left)/2+left;
        for (int i = 0; i < n; i++) {
            res += ints[i] / mid;
        }
        if (res<k){
            right = mid;
        }else if (res>k){
            left=mid;
        }
    }
  2. 根据所需要的精度,让之前求出的较优值去逐渐靠近最优质的

    while (true){//因为是求最大值,所以我们不断的去增加之前求出的较优值,直到这个值不再满足我们所需要的条件
        mid++;
        int res01 = 0;
        for (int i = 0; i < n; i++) {
            res01 += ints[i] / mid;
        }
        if (res01 != k){
            mid--;
            break;
        }
    }

完整代码

public static void main(String[] args) {
​
        //这部分操作是输入数据并将输入的 N 条绳子的长度 Len 存入一个整数数组里
        Scanner scanner = new Scanner(System.in);
        String str = scanner.nextLine();
        String[] strArr = str.split(" ");
        int n = Integer.parseInt(strArr[0]);
        int k = Integer.parseInt(strArr[1]);
        int[] ints = new int[n];
​
        //设置左右区间,将最短的那根绳子设为右边界
        int left = 1;
        int right = 10000;
        for (int i = 0; i < n; i++) {
            ints[i] = scanner.nextInt();
            if (ints[i] < right){
                right = ints[i];
            }
        }
        scanner.close();
​
​
        //改变left 和 right 的值,使最优解放在区间内
        int res = 0;
        for (int i = 0; i < n; i++) {
            res += ints[i] / right;
        }
        while (res>=k){
            res = 0;
            int temp = right;
            right = right*2;
            left = temp;
            for (int i = 0; i < n; i++) {
                res += ints[i] / right;
            }
        }
​
        //利用二分查找,找到一个较优的值
        int mid = 0;
        while (res != k){
            res = 0;
            mid = (right-left)/2+left;
            for (int i = 0; i < n; i++) {
                res += ints[i] / mid;
            }
            if (res<k){
                right = mid;
            }else if (res>k){
                left=mid;
            }
        }
​
        //因为求最大,所以逐渐将这一较优值增加,直到这一值不满足条件
        while (true){
            mid++;
            int res01 = 0;
            for (int i = 0; i < n; i++) {
                res01 += ints[i] / mid;
            }
            if (res01 != k){
                mid--;
                break;
            }
        }
        System.out.println(mid);
    }

总结

我在写代码的时候是按照我之前的步骤来的,在写完代码之后,我感觉这个代码写的有些冗余了,之后需要继续完善。

绳子切割的具体实现代码可以分为以下几个步骤: 1. 在游戏场景中添加触摸事件监听器,当玩家触摸到绳子时,获取触摸点的坐标。 ```javascript // 在onLoad函数中添加触摸事件监听器 this.node.on(cc.Node.EventType.TOUCH_START, function(event) { // 获取触摸点的坐标 var touchPos = event.getLocation(); // 将触摸点从世界坐标系转换为节点局部坐标系 var localPos = this.node.convertToNodeSpaceAR(touchPos); // 判断触摸点是否在绳子上 if (this.ropeNode.getBoundingBoxToWorld().contains(touchPos)) { // 切割绳子 this.cutRope(localPos); } }, this); ``` 2. 根据触摸点与绳子的交点,将绳子分成两段。 ```javascript // 切割绳子 cutRope: function(cutPos) { // 获取绳子组件 var rope = this.ropeNode.getComponent("Rope"); // 获取绳子的所有关节点 var joints = rope.joints; // 遍历所有关节点,找到距离触摸点最近的关节点 var minDist = Number.MAX_VALUE; var nearestJoint = null; for (var i = 0; i < joints.length; i++) { var dist = cc.pDistance(joints[i], cutPos); if (dist < minDist) { minDist = dist; nearestJoint = joints[i]; } } // 将绳子分成两段 rope.cut(nearestJoint); } ``` 3. 将绳子分成两段的具体实现可以在绳子组件中定义,其中需要修改绳子的关节节点和刚体属性。 ```javascript // 将绳子从指定的关节节点处切断 cut: function(joint) { // 获取绳子的刚体组件 var rigidbody = this.ropeNode.getComponent(cc.RigidBody); // 获取绳子的所有关节点 var joints = this.joints; // 获取指定关节节点的索引 var index = joints.indexOf(joint); // 创建两个新的绳子节点 var node1 = cc.instantiate(this.ropeNode); var node2 = cc.instantiate(this.ropeNode); // 将新的绳子节点插入到指定关节节点的位置 this.node.insertChild(node1, index); this.node.insertChild(node2, index + 1); // 获取新的绳子节点的刚体组件 var rigidbody1 = node1.getComponent(cc.RigidBody); var rigidbody2 = node2.getComponent(cc.RigidBody); // 获取新的绳子节点的物理关节组件 var joint1 = node1.getComponent(cc.PhysicsJointDistance); var joint2 = node2.getComponent(cc.PhysicsJointDistance); // 修改新的绳子节点的关节节点和刚体属性 var joints1 = joints.slice(0, index + 1); var joints2 = joints.slice(index + 1); node1.getComponent("Rope").joints = joints1; node2.getComponent("Rope").joints = joints2; rigidbody1.linearVelocity = rigidbody.linearVelocity.clone(); rigidbody2.linearVelocity = rigidbody.linearVelocity.clone(); rigidbody1.linearDamping = rigidbody.linearDamping; rigidbody2.linearDamping = rigidbody.linearDamping; joint1.connectedBody = rigidbody1; joint2.connectedBody = rigidbody2; joint1.distance = cc.pDistance(joints1[0], joints1[joints1.length - 1]); joint2.distance = cc.pDistance(joints2[0], joints2[joints2.length - 1]); // 删除原来的绳子节点 this.node.removeChild(this.ropeNode); } ``` 以上是绳子切割的基本实现方式,具体实现过程中还需要考虑绳子断裂后的物理效果和绘制效果等问题
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值