題目描述:
某財務部門結賬時發現總金額不對頭。很可能是從明細上漏掉了某1筆或幾筆。
如果已知明細賬目清單,能通過編程找到漏掉的是哪1筆或幾筆嗎?
如果有多種可能,則輸出所有可能的情況。
我們規定:用戶輸入的第一行是:有錯的總金額。
接下來是一個整數n,表示下面將要輸入的明細賬目的條數。
再接下來是n行整數,分別表示每筆賬目的金額。
要求程序輸出:所有可能漏掉的金額組合。每個情況1行。金額按照從小到大排列,中間用空格分開。
比如:
用戶輸入:
6
5
3
2
4
3
1
表明:有錯的總金額是6;明細共有5筆。
此時,程序應該輸出:
1 3 3
1 2 4
3 4
爲了方便,不妨假設所有的金額都是整數;每筆金額不超過1000,金額的明細條數不超過100。
本體使用的是遞歸思想,有幾個關鍵點和難點;
首先說關鍵點:
1.首先我們要讀懂題目,要求我們找出超過n元以外的金額的賬單,以示例來說,就是要求err_money=6,那麼我們就要找到幾筆賬單(這些賬單金額的和爲6元),然後依次輸出剩下的幾筆賬單的金額。所以本題兩種思路,第一種是dfs找出所有和爲6的結果,輸出數組剩下的元素;第二種是先算出所有賬單金額的總和,減去err_money(也就是一開始輸入的6),得出剩下的金額總和,再用dfs找出所欲和爲剩餘金額的方案,以題目的示例,就是找出和爲 (3+2+4+3+1)- 6 的所有方案。本次博客先介紹前者的方法,後者的方法下次再寫。
2.遞歸的思想:這裏簡單的說就是,找到一個變量或元素,決定選他或者不選他,這樣就會產生兩種情況,然後遞歸,記得回溯。
例如:本題中,我們選擇的數組num[] 3 2 4 3 1 中。假設我已經我選了num[0],那麼對於num[1]來說,我選擇了他並於num[0]相加,將會產生第一種情況,得出的和爲5;如果不選擇他與num[1]相加,那麼將會產生第二種情況,就是目前的和爲3。
3.要尋找到遞歸函數的幾個參數,首先需要想到有err_money的參數(也就是錯誤的金額的和),以此來確定是否到達要取的值,然後要想到,我如何才能讓我取的幾個值的和err_money比較,那我就需要一個current_money的變量與其比較(也就是時刻記錄金額的和),而這個current_money的變量我如何爲他賦值嘞?因爲題目可以認爲是一個數組形式(下一篇文章我會介紹將其當成一個集合的形式來寫),那麼我可以在此產生一個vis[]的數組來依次標記數組的元素,vis數組默認全是0,也就是未訪問的意思(vis是visit的縮寫,代表訪問數組,這個標記法應該是dfs的基礎標配了吧,具體可以點擊這裏),所以認爲的狀態就是,vis[i]是0,就未被訪問(沒有加入到current_money中),對應上面的第一種情況,如果被訪問了,就被加入變量了,視爲第二種方案。因爲使用dfs標記法需要回溯,所以每一次要記得將vis[i]重新置爲0。所以要加入數組num以及他的元素下標step,以此來不斷的找到數組的各個元素,然後分爲很多種情況。
難點:
1.題目中要求輸出所有可能漏掉的金額組合。金額按照從小到大排列。對於大小排列,可以用Arrays.sort()來解決
所以我們要注意是漏掉的組合,而不是所有情況,對於3 4 和 4 3 其實是算一種的,所以你跑起來可能會發現有兩行可能是一樣的,然後目前我想出來的方法使用StringBuffer來解決這個問題,但下面的代碼沒有寫上,將會在下一個博客中寫。
2.對於dfs函數的出口也很重要,出口條件位置放的不對,可能會使輸出亂七八糟,也可以試着遵循一個原則,對於step參數(就是會不斷+1遞歸的那個,本類型也可以粗略理解爲數組下標),最好就放在最下面,有些特別情況例外,因爲對於數組的判斷一般來說是最後的。
import java.util.Arrays;
import java.util.Scanner;
public class 財務部門結賬 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int err_money = sc.nextInt();//err_money 指一開始錯誤的財務總和
int current_money = 0;//current_money 指 每一步加或不加的財務總和
int n = sc.nextInt();//n 指賬目數量
int num [] = new int[n];//數組內的元素 指每個賬目的具體金額
int vis[] = new int[n];//vis數組 用來記錄當前的元素是否被訪問(即是否被加在current_money中)
int step = 0;//step 指num數組中的元素下標
for(int i = 0 ; i < num.length ; i++) {
num[i] = sc.nextInt();
}
Arrays.sort(num);
f(err_money,num,step, current_money,vis);
//起始狀況:f(err_money,num,0,0,0);
}
public static void f(int err_money, int[] num, int step, int current_money, int[] vis) {
if(current_money > err_money) return;
if(current_money == err_money) {
for(int i = 0 ; i < num.length ; i ++) {
if(vis[i] == 0) {
System.out.print(num[i]+" ");
}
}
System.out.println();
return;
}
if(step >= num.length) return;
vis[step] = 0;
f(err_money,num,step+1, current_money,vis);
vis[step] = 1;
f(err_money,num,step+1, current_money+num[step],vis);
vis[step] = 0;//回溯
}
}