DFS进阶——开心

开心
题目分析

基本思路:

对于这道题我们要遍历的是在哪一个位置上填“+”。那么我们可以定义一个数组vis[i],如果它为真表示我们在数字从左向右数第i个数字和第i+1个数字之间添加一个“+”。最后我们要判断所有的“+”是否都被用完了。

细节梳理:

因为我要遍历数字每一位上的值,所以在存数字的时候,我直接用字符数组存,这样取每一位上的值时比较方便。

Scanner sc = new Scanner(System.in);
n = sc.next();
k = sc.nextInt();
arr = n.toCharArray();
vis = new boolean[arr.length];

注意假设数字的长度为n,那么“+”不能放在位置n,因为我们要保证“+”的右边有数字,所以vis数组的结束位置应该是比n小1的地方。下面代码的end表示vis数组的结束位置。举个例子,比如1234,长度为4,我顶多在123的右边放个“+”变成123+4,即vis[2]=true,注意数组下标是从0开始的,当i遍历到3也就是vis.length - 1时,说明我该考虑的位置都考虑完了,接下来应该是求此方案下得到的数的值,所以end是结束位置。

 end = vis.length - 1; //vis能插入+的有效范围 n.length - 1

接下来我们考虑在某个位置上插入“+”。如下代码,这里是stp表示当前遍历到的位置下标,ki表示当前还剩下的可以插入的“+”数量。那么对于当前位置stp来说我可以选择插入“+”,也可以不插入“+”,当i=1时表示我选择的是插入,那么这里要注意,能插入的前提是我还有“+”可以使用,所以要判断ki是否大于0。要插入的话就把vis[stp]置为true。然后进入下一个位置的dfs,如果vis[stp]为true表示我插入了,那么“+”的数量ki就要-1.dfs结束之后,我要做下一个选择了,那么我要把之前选择记录的值复原,也就是把vis[stp]变为false。

static void DFS(int stp, int ki) {
    ......
    for (int i = 0; i < 2; ++i) { //0不插入+,1插入+
        vis[stp] = i == 1 && ki > 0;
        DFS(stp + 1, vis[stp] ? ki - 1 : ki);
        if (vis[stp]) vis[stp] = false;
    }
}

接下来,如果我把所有位置都判断完了是否添加“+”时,我要求当前情况下得到的值。

static void DFS(int stp, int ki) {
        if (stp == end) {//遍历到了终点
            if (ki == 0) { //当使用“+”都用完了才合法
                long sum = 0;
                long ans = 0;
                for (int i = 0; i < arr.length; ++i) {
                    ans = ans * 10 + (arr[i] - '0');
                    if (vis[i]) {//如果出现了“+”,则加上之前的数字,然后ans重置为0.
                        sum += ans;
                        ans = 0;
                    }
                }
                sum += ans; //加上最后一个数字
                if (sum < min) min = sum;//记录最小值
                if (sum > max) max = sum;//记录最大值
            }
            return;
        }
       .....
    }

上述过程举个例子。假设原始数字是1234,k=1,那么vis[1]=true

i=0 ans=1;i=1 ans=12 sum=sum+ans=12 ans=0;i=2 ans=3;i=3 ans=34;for循环结束,但是最后的34我没有加上,我要加上,所以有sum+=ans=sum+34=12+34=46

题目代码
import java.util.Arrays;
import java.util.HashSet;
import java.util.Scanner;

public class Main{
    
    static String n;
    static int k, end;
    static char[] arr;
    static boolean[] vis;
    static long min = Long.MAX_VALUE, max = 1;
    static HashSet<String> set = new HashSet<>();

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.next();
        k = sc.nextInt();
        arr = n.toCharArray();
        vis = new boolean[arr.length];
        end = vis.length - 1; //vis能插入+的有效范围 n.length - 1
        sc.close();
        if (n.length() > 1) {
            DFS(0, k);
            System.out.println(max - min);
        } else {
            System.out.println(0);
        }
    }
    
    static void DFS(int stp, int ki) {
        if (stp == end) {
            if (ki == 0) { //当使用完全才合法
                long sum = 0;
                long ans = 0;
                for (int i = 0; i < arr.length; ++i) {
                    ans = ans * 10 + (arr[i] - '0');
                    if (vis[i]) {
                        sum += ans;
                        ans = 0;
                    }
                }
                sum += ans; //加上最后一段
                if (sum < min) min = sum;
                if (sum > max) max = sum;
            }
            return;
        }
        for (int i = 0; i < 2; ++i) { //0不插入+,1插入+
            vis[stp] = i == 1 && ki > 0;
            DFS(stp + 1, vis[stp] ? ki - 1 : ki);
            if (vis[stp]) vis[stp] = false;
        }
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值