[藍橋杯2018初賽]倍數問題


題目描述

眾所周知,小蔥同學擅長計算,尤其擅長計算一個數是否是另外一個數的倍數。
但小蔥只擅長兩個數的情況,當有很多個數之后就會比較苦惱。
現在小蔥給了你 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 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM