算法训练 和为T JAVA编程

问题描述
  从一个大小为n的整数集中选取一些元素,使得它们的和等于给定的值T。每个元素限选一次,不能一个都不选。
输入格式
  第一行一个正整数n,表示整数集内元素的个数。
  第二行n个整数,用空格隔开。
  第三行一个整数T,表示要达到的和。
输出格式
  输出有若干行,每行输出一组解,即所选取的数字,按照输入中的顺序排列。
  若有多组解,
优先输出不包含第n个整数的;若都包含或都不包含,优先输出不包含第n-1个整数的,依次类推。
  最后一行输出总方案数。
样例输入
5
-7 -3 -2 5 9
0
样例输出
-3 -2 5
-7 -2 9
2
数据规模和约定
  1<=n<=22
  T<=maxlongint
  集合中任意元素的和都不超过long的范围

分析:1.这道题的主题思想就是深搜(DFS),一个数字要没选,要么不选,那么一个数就可以分出两个分支,就这样递归下去,枚举出每一种情况。 2.而且设置了输出顺序,也就是从左往右,越靠后的数字权重越大,只要含有权重大的数,如上面的-3 -2 5,-7 -2 9,因为后面那组数包含了9,所以权重更大,那么后面那组数必须排在后面。

所以综上所述,不仅要深搜,而且还要找个排序方法。

当时看到过有倒着搜索的解题方法,但是理解起来相对复杂,不容易理解。

当时就想到了把一组和为T的数放进链表,再把链表放进一个对象中捆绑储存(这个对象包含了本组数的字符序列,也就是一个字符串,还有本组数的权重和,score),我们只要把字符序列中的值所对应的权重相加在一起,就知道了本组合格的数的权重和,再根据权重和进行排序,小的在前,大的在后,轻松的解决了排序问题,不需要做什么倒序搜索之类的。

import java.util.Scanner;
import java.util.PriorityQueue;

public class Main {
    
    static class Tuple implements Comparable<Tuple> { //实现一个Comparable接口,用来比较每个和为T对象的权重大小
        int score;   //储存和为T的权重和
        String data; //储存本组数

        public Tuple(int score, String data) {
            this.score = score;
            this.data = data;
        }

        @Override
        public int compareTo(Tuple o) {
            return score - o.score;
        }
        
    };
    
    static PriorityQueue<Tuple> result = new PriorityQueue<Tuple>();
    //优先级队列的好处是自动会调用Comparable的接口实现比较大小,而且效率较高,使代码更简洁。
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);

        int n = input.nextInt();

        int a[] = new int[n];  //输入的数组
        int b[] = new int[n];  //用来递归的数组

        for (int i = 0; i < n; i++) {
            a[i] = input.nextInt();
        }

        int sum = input.nextInt();

        dfs(0, a, b, n, sum);//遍历起点
        
        
        
        for (Tuple tuple : result) {
            System.out.println(tuple.data);//直接遍历 输出和为T字符串
        }
            System.out.println(result.size());//输出符合题目的数组数量
    }

    public static void dfs(int now, int a[], int b[], int n, int sum) {
        if (now == n) {//当当前值now等于数组长度时 说明已经到底 可以进行判断了

            int temp = 0;
            for (int i = 0; i < n; i++) {
                if(b[i]!=-8888)//跳过识别码-8888
                temp += b[i];
            }
            if (temp == sum) {
                StringBuilder sb = new StringBuilder();//可变的字符序列
                int score = 0;
                for (int i = 0; i < n; i++) {
                    if(b[i]!=-8888) {//例如 1 -8888 2 -8888 3
                        sb.append(b[i]).append(" ");
                        score += (1 << i);//使用的进位符 也就是二进制
                        //当遇到-8888时跳过 那么1的权重为1 2的权重为4 3的权重为16 加起来就是权重和
                        //保证后一个数 要比前面加起来总和还要大 才能保证正确
                    }
                }
                if (score > 0)//排除所有数都没有选 全是-8888 但是相加为0 如果sum也是0 那么也会输出 所以排除权重和为零的情况
                    result.add(new Tuple(score, sb.toString()));//符合的,收入一个对象,每个对象又会放入优先级队列
            }

            return;
        }

        b[now] = a[now]; //模拟选择了这个数
        dfs(now + 1, a, b, n, sum);//进入递归函数

        b[now] = -8888; //模拟没有选择这个数 相当于一个识别码 如果直接传入0作为不选的话 虽然理解起来方便,但是会跟其他数据冲突
        dfs(now + 1, a, b, n, sum);
    }
}

注:这个代码只能得到90分,但是我看到输入,和输出。跟别人的100代码比较,输出是完全一样的,那第九个测试数据不知道为什么不对。

第九组数据:

22

1 -1 2 -2 4 -4 8 -8 16 -16 32 -32 64 -64 128 -128 256 -512 512 -1024 -256 1024

0

和为T的个数有 2047 个。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章