SRM538-div1-2-TurtleSpy


题目大意:
     机器人从起始点开始,可以有个四种动作:
  • 前进X距离
  • 后退X距离
  • 向左旋转X度(1到359之间)
  • 向右旋转X度(1到359之间)
     现在给出一组动作的集合,动作的顺序未知,求该组动作按照一定的次序排列后得到的最大移动距离。
     数据规模:X为[1,1000],动作集合大小为[1,50]
     
     
思路:
     求解这道题的关键在于要观察到如下特点:
  • 旋转的动作是可选的,因为可以把这些旋转动作放在最后,那么对最终位置就不会有任何影响。
  • 最优移动策略中,前进的动作应该是连续的,后退的动作也应该是连续的。也就是说最优路径应该如下图所示,其中蓝色是前进的路线,红色是后退的路线。其中theta夹角表示两条路线之间旋转过的角度。


     第二个特点,看起来比较直观。凭直觉的话,不同的前进(或者后退)动作之间如果加入了旋转动作,那么走出来的路线是曲折的路线,距离必然会小于走直线的路线,所以最优路线应该是先往一个方向前进再往一个方向后退。虽然该命题是正确的(因为通过system test了),但是这种分析并不严谨,需要更加严格的证明,目前本人没想出证明方法。
     建立在这两个特点之上,我们所要解决的问题其实就是找到一个合适的theta使得最终距离最大,根据余弦定理:
                         c=sqrt(a^2+b^2-2*a*b*cost(theta))
theta越接近180度,c的值越大。所以我们需要从所有旋转角度的动作中找到一个组合使得theta的值最接近180度。
     由于角度的取值范围只能是[0,359]中的整数,所以可以用dp算法获得所有可能的角度值,然后选取其中最接近180度的。dp过程与背包问题类似,每考虑一个旋转动作记录一次已有的角度值即可。算法复杂度O(360*n),其中n为旋转动作数。

Java代码:

public class TurtleSpy {
    public double maxDistance(String[] commands) {
        long f = 0, b = 0;
        ArrayList<Integer> angle = new ArrayList<Integer>();
        for (int i = 0; i < commands.length; ++i) {
            String[] ss = commands[i].split(" ");
            int x = Integer.parseInt(ss[1]);
            if (ss[0].equals("forward")) {
                f += x;
            } else if (ss[0].equals("backward")) {
                b += x;
            } else if (ss[0].equals("left")) {
                angle.add(-x);
            } else {
                angle.add(x);
            }
        }
        boolean[] dp = new boolean[360];
        dp[0] = true;
        for (int i = 0; i < angle.size(); ++i) {
            boolean[] next = new boolean[360];
            for (int a = 0; a < 360; ++a){
                if (dp[a]) {
                    next[(a + 360 + angle.get(i)) % 360] |= dp[a];
                    next[a] |= dp[a];
                }
            }
            dp = next;
        }
        int theta = 0;
        for (; theta < 180; ++theta) {
            if (dp[180 - theta] || dp[180 + theta]) {
                break;
            }
        }
        return Math.sqrt(Math.abs(f * f + b * b + 2 * f * b * Math.cos(Math.PI * theta / 180)));
    }
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值