首页 > 试题广场 >

反转硬币

[编程题]反转硬币
  • 热度指数:471 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
在桌子上有 n 个硬币排成一排,从左到右依次是第一个,第二个,第三个......
硬币有正反两面,正面朝上用 0 表示,反面朝上用 1 表示。初始的时候桌子上的硬币正面反面都有,这样看起来就有点杂乱了。
牛牛决定整理一下这些硬币,让它们都正面朝上,会有一种规则的美感。
整理的规则是:假如现在有 k 个反面朝上的硬币,那么就将位置 k 处的硬币反转过来。
牛牛一直重复上面这个步骤,例如对于 3 个硬币的情况:
开始共有 2 个反面朝上的,所以反转第 2 个硬币,现在变成了
现在共有 3 个反面朝上的,所以反转第 3 个硬币,现在变成了
现在共有 2 个反面朝上的,所以反转第 2 个硬币,现在变成了
现在共有 1 个反面朝上的,所以反转第 1 个硬币,现在变成了
经过这四次操作之后,硬币终于如愿的全部正面朝上了。
现在给出硬币的序列,请你告诉牛牛共需要几次操作才能将所有的硬币都变得正面朝上,如果按这种方式永远也不可能变成全部正面朝上的话也要告诉牛牛哦。

输入描述:
第一行输入一个正整数 n,代表硬币的个数
接下来一行一个长度为 n 串,代表硬币的序列
1 \leq n \leq 10^5




输出描述:

如果可以在有限次内将硬币全部反转成正面朝上,输出这个次数
否则在一行中输出


示例1

输入

3
101

输出

4
思路和另外几位略有差异
通过基本法则确立了序列需要的步骤数和子序列所需步骤数的关系,一开始采用了递归的想法,但在具体实现时又去掉了递归转为while循环
(1) 000…01(n位)--(n-1)次--> 111…11 --n次--> 000…00
(2) 假定序列[…]需要x次操作变为[0…0](全0),有
     I. 11…11[…]--x次-->11…11[0…0]
     II. 0[…]00…01--x次-->0[0…0]00…01
     因此,较长序列的处理可以递归地转化到子序列的处理上
 (3) 序列的后缀0可以忽略


import java.math.BigInteger;
import java.util.Scanner;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int[] b = new int[n];
        String s = in.next();

        for (int i = 0; i < n; i++) {
            b[i] = (s.charAt(i) == '1') ? 1 : 0;
        }

        /** 算法设计依据
         * (1) 000…01(n位)--(n-1)次--> 111…11 --n次--> 000…00
         * (2) 假定序列[…]需要x次操作变为[0…0](全0),有
         *     I. 1[…]--x次-->1[0…0]
         *     II. 0[…]00…01--x次-->0[0…0]00…01
         *     因此,较长序列的处理可以递归地转化到子序列的处理上
         * (3) 序列的后缀0可以忽略
         */

        int left = 0;
        int right = b.length;

        // 双指针遍历数组
        while (left < right) {
            if (b[left] == 0) {
                // 如果左指针指向0,则需要找到右侧最近的1来配对
                right--; // 右指针左移

                // 继续移动右指针直到找到1或与左指针相遇
                while (right > left && b[right] != 1) {
                    right--;
                }

                // 如果左右指针相遇,说明没有可配对的1,结束循环
                if (right == left) {
                    break;
                }

                // 计算当前0和右侧1之间的距离(包含两端)
                int distance = right - left + 1;
                b[left] = -distance; // 在左指针位置标记负的距离值
            }
            left++; // 左指针右移
        }

        // 计算最终结果
        BigInteger t = BigInteger.ZERO;
        // 从右向左遍历处理后的数组
        for (int i = left - 1; i >= 0; i--) {
            if (b[i] > 0) {
                // 遇到1,结果加1
                t = t.add(BigInteger.ONE);
            } else {
                // 遇到标记的负距离值,计算对应的贡献值
                // 公式:-b[i] * 2 - 1(因为距离为d时贡献值为2d-1)
                t = t.add(BigInteger.valueOf(-b[i] * 2 - 1));
            }
        }
        System.out.println(t); // 输出最终结果
    }
}


发表于 2025-06-16 22:27:04 回复(0)