第八屆藍橋杯決賽 對局匹配
標題:對局匹配
小明喜歡在一個圍棋網站上找別人線上對弈。這個網站上所有註冊使用者都有一個積分,代表他的圍棋水平。
小明發現網站的自動對局系統在匹配對手時,只會將積分差恰好是K的兩名使用者匹配在一起。如果兩人分差小於或大於,系統都不會將他們匹配。
現在小明知道這個網站總共有名使用者,以及他們的積分分別是。
小明想了解最多可能有多少名使用者同時線上尋找對手,但是系統卻一場對局都匹配不起來(任意兩名使用者積分差不等於)?
輸入
第一行包含兩個個整數N和K。
第二行包含N個整數A1, A2, … AN。
對於30%的資料,1 <= N <= 10
對於100%的資料,1 <= N <= 100000, 0 <= Ai <= 100000, 0 <= K <= 100000
輸出
一個整數,代表答案。
樣例輸入:
10 0
1 4 2 8 5 7 1 4 2 8
樣例輸出:
6
樣例輸入:
10 1
2 1 1 1 1 4 4 3 4 4
樣例輸出:
8
資源約定:
峰值記憶體消耗 < 256M
CPU消耗 < 1000ms
請嚴格按要求輸出,不要畫蛇添足地列印類似:“請您輸入…” 的多餘內容。
所有程式碼放在同一個原始檔中,除錯通過後,拷貝提交該原始碼。
注意: main函式需要返回0
注意: 只使用ANSI C/ANSI C++ 標準,不要呼叫依賴於編譯環境或作業系統的特殊函式。
注意: 所有依賴的函式必須明確地在原始檔中 #include , 不能通過工程設定而省略常用標頭檔案。
提交時,注意選擇所期望的編譯器型別。
思路
如果把個元素按照將分數相差為k的使用者分成一組,例如第一組就是,第二組就是,等等。這樣分組的話,每個分組的使用者是不可能和其他分組的使用者匹配成功的,因為分差不可能為。
這樣的話,只要在每個分組裡面選取儘量多的使用者就可以了。用表示分數為的使用者人數,假設現在第組有個不同分數,其中表示該組第一個人的積分,那麼用動態規劃法來選擇儘量多的人數。表示選擇前個分數能獲得的最大使用者人數(價值),很明顯如果選擇第個分數,那麼第個分數是不能選的,因為它們的積分相差,該組最大線上人數為。
狀態轉移方程如下:
其中表示積分為第個分數的總人數。是否感覺上述動態方程與01揹包很類似?
需要注意的是,要特殊處理。
演算法複雜度
,只與最大分數有關。
AC程式碼
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define MAX_SCORE 100000
const int maxn = 100000 + 5;
int cnt[MAX_SCORE+5], val[maxn], dp[maxn];
int n, k;
int main() {
while(scanf("%d%d", &n, &k) == 2) {
memset(cnt, 0, sizeof(cnt));
int score, ans = 0;
for(int i = 1; i <= n; i++) {
scanf("%d", &score);
cnt[score]++;
}
//特殊處理k=0的情況
if(k == 0) {
for(int i = 0; i <= MAX_SCORE; i++) {
if(cnt[i]) ans++;
}
}
else {
for(int i = 0; i < k; i++) {
int m = 0;
for(int j = i; j <= MAX_SCORE; j+=k) {
val[m++] = cnt[j];
}
dp[0] = val[0];
for(int j = 1; j < m; j++) {
if(j == 1) dp[j] = max(dp[0], val[j]);
else dp[j] = max(dp[j-2] + val[j], dp[j-1]);
}
ans += dp[m-1];
}
}
printf("%d\n", ans);
}
return 0;
}
如有不當之處歡迎指出!