試題 歷屆試題 對局匹配
資源限制
時間限制:1.0s 內存限制:256.0MB
問題描述
小明喜歡在一個圍棋網站上找別人在線對弈。這個網站上所有註冊用戶都有一個積分,代表他的圍棋水平。
小明發現網站的自動對局系統在匹配對手時,只會將積分差恰好是K的兩名用戶匹配在一起。如果兩人分差小於或大於K,系統都不會將他們匹配。
現在小明知道這個網站總共有N名用戶,以及他們的積分分別是A1, A2, … AN。
小明想了解最多可能有多少名用戶同時在線尋找對手,但是系統卻一場對局都匹配不起來(任意兩名用戶積分差不等於K)?
輸入格式
第一行包含兩個個整數N和K。
第二行包含N個整數A1, A2, … AN。
輸出格式
一個整數,代表答案。
樣例輸入
10 0
1 4 2 8 5 7 1 4 2 8
樣例輸出
6
數據規模與約定
對於30%的數據,1 <= N <= 10
對於100%的數據,1 <= N <= 100000, 0 <= Ai <= 100000, 0 <= K <= 100000
試題解析
我們要求的是無法匹配的最大人數,它的匹配機制是積分差值爲k的可以匹配成功即x和x+k這兩種積分的用戶可以匹配成功他與其他任意積分的用戶都無法匹配,所以我們對用戶進行這樣的分組{x,x+k,x+2k,x+3k…}(x∈[0,k))這樣每個小組無論我們選誰不同小組之間的人一定無法匹配成功,所以求出每個小組的最大人數在加起來就是問題的答案。下面分析如何求:
如上圖opt(i)代表小組前i(包含i)種積分的最優選法,v(i)代表第i種積分:例如我們先考慮前5個那麼第五種積分存在兩種情況一種是選一種是不選,如果不選那麼opt(5)=opt(4),如果選那麼第4種積分的人就不能參加匹配opt(5)=v5+opt(3),所以opt(5)=max(opt(4),v5+opt(3))改成公式就是
opt(i)=max(opt(i-1),vi+opt(i-2))(i>1)所以我們只需要求出opt(1)和opt(0)就能通過公式找到當前小組的最優解。
可以用遞歸(如果用遞歸上圖可以看出v5選的時候又要求opt(3)其實我們在求opt(5)左分支的時候已經求一次opt(3)了,所以使用遞歸需要進行記憶優化一下)也可以用動態規劃。下面代碼使用的是動態規劃。(兩者都是基於公式)
代碼
#include<stdio.h>
// n,k和題意相同;max代表當前最大的積分;integral代表積分;count統計人數
int n,k,max=0,integral,count=0;
// arr存儲積分;arr1存儲分組的小組;dp動態規劃
int arr[100000]={0},arr1[100000]={0},dp[100000];
// 求最大值函數
int max_num(int x,int y){
if(x>=y){
return x;
}
return y;
}
int main(){
scanf("%d%d",&n,&k);
int i,j;
for(i=0;i<n;i++){
scanf("%d",&integral);
arr[integral]++;// arr數組的下標表示積分;值表示該積分的人數
if(arr[integral]==1){
count++;// 統計共多少種不同的分數
}
if(max<integral){
max=integral;// 存儲最大積分
}
}
if(!k){// k=0的情況就是每種分數選1個人
printf("%d",count);
return 0;
}
count=0;// 初始化count
for(i=0;i<k;i++){
int num=0;
j=i;
while(j<=max){// arr1存儲分配的小組
arr1[num++]=arr[j];
j+=k;
}// dp[j]表示前j個能得到的滿足題意的最大人數;入口爲:dp[0]=arr[0],dp[1]=arr[1];
for(j=0;j<num;j++){
if(j<2){
dp[j]=arr1[j];
continue;
}// 選取當前最優解
dp[j]=max_num(dp[j-1],dp[j-2]+arr1[j]);
}// dp[num-1]就是當前小組的最大人數
count+=dp[num-1];
}
printf("%d",count);
return 0;
}