題目描述
眾所周知,小蔥同學擅長計算,尤其擅長計算一個數是否是另外一個數的倍數。
但小蔥只擅長兩個數的情況,當有很多個數之后就會比較苦惱。
現在小蔥給了你 n 個數,希望你從這 n 個數中找到三個數
使得這三個數的和是 K 的倍數,且這個和最大。數據保證一定有解。
輸入
第一行包括 2 個正整數 n, K。
第二行 n 個正整數,代表給定的 n 個數。
1 <= n <= 10^5, 1 <= K <= 10^3,給定的 n 個數均不超過 10^8。
輸出
輸出一行一個整數代表所求的和。
樣例輸入
4 3 1 2 3 4
樣例輸出
9
分析:首先看這個題,從n個數中選三個數,且三個數相加的和是k的倍數,且滿足倍數中最大。
那么這一看就是背包空間3,選數滿足特殊要求,的01背包吧。
現在分析怎么才能構成k的倍數, 假設我們 k=3,要選一個數4,要讓它構成3的倍數,然后要選另一個數,
那么另一個數要讓它余數為零,則要找余數為(0 – x%k + k)%k的數,則推出兩個數相加要求余數不變,則滿足(當前余數 - x%k + k)%k的數。
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <vector> 5 #include <iostream> 6 using namespace std; 7 8 const int N = 1010; 9 10 int n, m; 11 vector<int> a[N]; 12 int f[4][N];//4代表當前選的數,N代表余數 13 14 int main() 15 { 16 scanf("%d%d", &n, &m); 17 18 for (int i = 0; i < n; i ++ ) 19 { 20 int x; 21 scanf("%d", &x); 22 a[x % m].push_back(x);//將數按余數分組 23 } 24 25 memset(f, -9999, sizeof f); 26 f[0][0] = 0; 27 28 for (int i = 0; i < m; i ++ )//枚舉余數背包 29 { 30 sort(a[i].rbegin(), a[i].rend());//從大到小排序 31 32 for (int u = 0; u < 3 && a[i].size() > u; u ++ )//每個分組最多選三個最大的 33 { 34 int x = a[i][u]; 35 36 for (int j = 3; j >= 1; j -- ) 37 for (int k = 0; k < m; k ++ ){//枚舉余數 38 39 f[j][k] = max(f[j][k], f[j - 1][(k - x%m + m) % m] + x); 40 //f[j - 1][(k - x%m + m) % m] + x保證相加后的余數還為k 41 } 42 } 43 } 44 45 printf("%d\n", f[3][0]);//輸出我們要的余數為零的數 46 47 return 0; 48 } 49