经典dfs剪枝问题-木棒

 

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.StringTokenizer;

/**
 * @Author congge
 * @Date 2024/3/14 22:05
 * @description https://www.acwing.com/problem/content/169/
 */
public class Main {
    static FastReader sc = new FastReader();
    static PrintWriter out = new PrintWriter(System.out);
    static int N = 70;
    static int n,sum,len;
    static int[] a = new int[N];
    static boolean[] st = new boolean[N];

    public static boolean dfs(int u,int cur,int start){
        if (u * len == sum) return true;

        //这里不是直接调用dfs(u + 1,0,0),而是区凑下一根木棒,要把所有木板一直凑到u * len == sum
        //并且每一根都得凑成功
        if (cur == len) return dfs(u + 1,0,0);

        //从编号为start的木板开始凑,防止出现冗余答案
        for(int i = start;i < n;i++){
            if (st[i]) continue;    //用过的木棒不能用

            if (a[i] + cur <= len){
                //这种情况是满足条件的,继续凑
                st[i] = true;
                //往下凑,看看能不能凑到
                if (dfs(u,cur + a[i],i + 1)) return true;
                //如果凑到了就算是一个答案,没凑到就恢复现场
                st[i] = false;
            }

            //这里是没凑到的情况
            //如果是第一个木棒放进去凑不了,那么以后一定会出现要把该木棒放在其他木棒的第一个位置
            //同理,其他位置也容不下第一个木棒,那么以a[i]作为第一个木棒的答案都返回失败
            if (cur == 0)return false;

            //最后一个木板虽然匹配上了,但是后面的木棒凑不起来
            //因为是从大到校枚举,4 后面可以是 2 + 2
            //虽然4满足题意,2 + 2也满足题意
            //如果把2 + 2放在这里,那么后面的4如果遇上两根木棒都剩下2就凑满的情况,就匹配不上了
            if (a[i] + cur == len) return false;

            //如果是中间没匹配上,那没关系,后面还有选择的机会
            //就比如做一件事,第一步就错了,后面就没必要继续了
            //最后一步对了,但是其他事干不成了,那么也不用去做其他事了

            //但是中间错了,还有其他补救机会

            //第a[i]数放进去没用,那么与a[i]相等的数就不用再判断了
            int j = i + 1;
            while (j < n && a[j] == a[i]) j++;
            i = j - 1;
        }
        return false;
    }

    public static void main(String[] args) {
        while (true){
            n = sc.nextInt();
            if (n == 0) break;
            sum = len = 0;
            for(int i = 0;i < n;i++){
                a[i] = sc.nextInt();
                sum += a[i];
                len = Math.max(len,a[i]);
            }
            Arrays.sort(a,0,n);
            for(int i = 0,j = n - 1;i < j;i++,j--){
                int temp = a[i];
                a[i] = a[j];
                a[j] = temp;
            }
            while (true){
                if (sum % len == 0 && dfs(0,0,0)){
                    out.println(len);
                    break;
                }
                len++;
            }
            Arrays.fill(st,0,n,false);
        }
        out.flush();
    }
}

class FastReader {
    StringTokenizer st;
    BufferedReader br;

    FastReader() {
        br = new BufferedReader(new InputStreamReader(System.in));
    }

    String next() {
        while (st == null || !st.hasMoreElements()) {
            try {
                st = new StringTokenizer(br.readLine());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return st.nextToken();
    }

    int nextInt() {
        return Integer.parseInt(next());
    }

    String nextLine() {
        String s = "";
        try {
            s = br.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return s;
    }

    long nextLong() {
        return Long.parseLong(next());
    }

    double nextDouble() {
        return Double.parseDouble(next());
    }

    // 是否由下一个
    boolean hasNext() {
        while (st == null || !st.hasMoreTokens()) {
            try {
                String line = br.readLine();
                if (line == null)
                    return false;
                st = new StringTokenizer(line);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return true;
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值