1. 程式人生 > >小Q的歌單_騰訊2018春招技術類程式設計題

小Q的歌單_騰訊2018春招技術類程式設計題

[程式設計題] 小Q的歌單

時間限制:1秒

空間限制:32768K

小Q有X首長度為A的不同的歌和Y首長度為B的不同的歌,現在小Q想用這些歌組成一個總長度正好為K的歌單,每首歌最多隻能在歌單中出現一次,在不考慮歌單內歌曲的先後順序的情況下,請問有多少種組成歌單的方法。

輸入描述:

每個輸入包含一個測試用例。
每個測試用例的第一行包含一個整數,表示歌單的總長度K(1<=K<=1000)。
接下來的一行包含四個正整數,分別表示歌的第一種長度A(A<=10)和數量X(X<=100)以及歌的第二種長度B(B<=10)和數量Y(Y<=100)。保證A不等於B。

輸出描述:

輸出一個整數,表示組成歌單的方法取模。因為答案可能會很大,輸出對1000000007取模的結果。

輸入例子1:

5
2  3  3  3

輸出例子1:

9

程式碼

import java.util.Scanner;
 
public class Main{

    public static long[][] arr = new long[101][101];

    public static void main(String[] args){
        Scanner scan = new Scanner(System.in);
        int k = scan.nextInt();
        int A = scan.nextInt();
        int X = scan.nextInt();
        int B = scan.nextInt();
        int Y = scan.nextInt();
        Initial(arr);
        System.out.println(Calculate(k,A,X,B,Y));
    }

    public static void Initial(long[][] array){
		array[0][0] = 1;
		for(int i = 1;i <= 100;i++){
			array[i][0] = 1;
			for(int j = 1;j <= 100;j++){
				array[i][j] =( array[i-1][j-1] + array[i-1][j])%1000000007;;
			}
		}
	}
     
    public static long Calculate(int K, int A, int X, int B, int Y){
        long result = 0;
        int length;
        for (int i = 0; i <= X; i++){
            length = K - A * i;
            if (length >= 0 && length % B == 0 && length / B <= Y){
                result += (arr[X][i] * arr[Y][length / B])% 1000000007;
            }
        }
        return result % 1000000007;
    }
}

提交結果

您的程式碼已儲存
答案正確:恭喜!您提交的程式通過了所有的測試用例

解題思路

       由於不需要考慮歌單內歌曲的先後順序,且每首歌只能出現一次,則該問題可以看成以下取多少種歌曲組合最後組成某個長度的歌單,歌曲之間相互獨立。比如需要組合的歌單長為5,現有3首歌曲長度為2和3首歌曲長度為3的不同歌曲,可以選取1首歌曲長度為2的歌曲和1首歌曲長度為3的歌曲,歌曲長度為2的歌曲可以選擇的物件有3種,歌曲長度為3的歌曲可供選擇的物件也為3種,3乘以3得9,故組合的歌單種類為9種。

       利用暴力迴圈求得所有歌單長度的組合方法,假設選取A類歌曲的個數從0到最大值,然後根據這個A類歌曲選取值選取求得B類歌曲選取個數,注意並不一定所有A類歌曲的選擇個數都可以恰好組合成歌單,判斷條件是選取的B類歌曲個數恰好為整數,且不大於B類歌曲個數最大值。

      剩下的問題就是求得從M個不同小球中取得N個小球的組合數,在數學中可以記作為 C_{n}^{m} ,由於最後得數的數量級有點大,在一開始的時候遇到了點問題,利用數學中常見方式求得該組合數值無法得出正確的測試結果,當M=10,N=4時,計算方法是result = 10*9*8*7/(4*3*2*1),該方法在是沒有問題但最後ACE的結果老無法得到正確結果。最後分析原因,是因為題目中要求輸出對1000000007取模的結果。

    public static long calculate(long x,long i){
		long mod = 1000000007;
		long result = 1;
		if(i > (x/2))
			i = x-i;
		if(x < i){
			return 0;
		}else if(x == i){
			return 1;
		}else {
			for(long k = 1;k <= i;k++){
				result = (result * (x - k + 1) / k)%mod;
				System.out.println(result);
			}
			return result/mod;
		}
    }

當乘積的結果不是很大的時候以上程式碼計算值是符合題目要求的,但是一旦超過long的範圍輸出值則不符合要求,被乘數每次取模必然是不對的,只有通過加法求得各種組合數才是符合題目要求的,參見正確程式碼中Initial函式。使用相乘法求組合數最大的ace正確率只能達到70%。

通過加法求解組合數是01揹包問題,這裡就不再概述了。