算法訓練 和爲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 個。




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