# 題目: UVa 10616 - Divisible Group Sums

# 題目說明

給一個有 N 個數字的序列,取出 M 個數字相加,使結果能整除 D ,求總方法數


INPUT:
每筆測資第一行輸入兩個整數 NQ
接下來有 N 行,每行輸入一個整數
接下來有 Q 行,每行輸入兩個整數 DM
NQ0 時結束程式


OUTPUT:
輸出符合題目條件的總方法數

# 解題方法

此題為 knapsack problems (背包問題)

需要先將輸入的 N 個數字 ( num[i] ) 做處理

  1. 若為正數num2[i] = num[i] % D ,直接取 D 的餘數
  2. 若為負數num2[i] = num[i] % D + D ,取 D 的餘數後加 D ,使之變正數

定義一個 dp[j][k]
j 為當前取到的數值
k 為取幾個數

轉移方程為 dp[j][k] += dp[j - num2[f]][k - 1]
最後將所有 dp[D的倍數][M] 相加為答案

由於題目限制為 32 bit 的有號整數,所以需要用 long long 以防 overflow

# 參考程式碼

#include <iostream>
#include <vector>
#include <memory.h>
using namespace std;
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	int N, Q, D, M;
	int a, cases = 0;
	long long dp[201][11];
	while (cin >> N >> Q, N && Q)
	{
		vector<int> num;
		while (N--) cin >> a, num.emplace_back(a);
		cout << "SET " << ++cases << ":\n";
		for (int i = 1; i <= Q; ++i)
		{
			cin >> D >> M;
			vector<int> num2;
			long long ans = 0;
			memset(dp, 0, sizeof(dp));
			for (auto& j : num) num2.emplace_back(j % D > 0 ? j % D : j % D + D);
			dp[0][0] = 1;
			for (int f = 0; f < num2.size(); ++f) for (int j = 200; j >= num2[f]; --j)
				for (int k = M; k > 0; --k) dp[j][k] += dp[j - num2[f]][k - 1];
			for (int j = 0; j <= 200; j += D) ans += dp[j][M];
			cout << "QUERY " << i << ": " << ans << "\n";
		}
	}
	return 0;
}

# 參考資料

https://blog.csdn.net/mobius_strip/article/details/41252307
https://summon528.github.io/2017/12/05/UVA-10616-Divisible-Group-Sums/