提交此题 评测记录
资源限制
时间限制:1.0s 内存限制:256.0MB
问题描述
小明喜欢在一个围棋网站上找别人在线对弈。这个网站上所有注册用户都有一个积分,代表他的围棋水平。
小明发现网站的自动对局系统在匹配对手时,只会将积分差恰好是K的两名用户匹配在一起。如果两人分差小于或大于K,系统都不会将他们匹配。
现在小明知道这个网站总共有N名用户,以及他们的积分分别是A1, A2, … AN。
小明想了解最多可能有多少名用户同时在线寻找对手,但是系统却一场对局都匹配不起来(任意两名用户积分差不等于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
思路:
蓝桥杯还真一题变一个算法啊,开始用dfs()深搜,只能过一个测试,后面就算不出来了。又补了dp算法,看了好几篇文章才写出来了。
-
首先,进行分组,从0-(K-1) 之间的每一个分值各分成一组(score%k),例如:
0, K, 2K, 3K, … ,nK; 1, 1+K, 1+2K, 1+3K, … ,1+nK; 2, 2+K, 2+2K, 2+3K, … ,2+nK;
这样有两个特点:
- 组与组之间的分数肯定不能匹配
- 组内相邻分数可以匹配,相间分数不可匹配
- 然后,对每一组用dp来求最大人数。对于前 j 个分数,可以拆分为是否加入第 j 个分数?
-
是。 most_p = most_p[n-2] + val[j] 前 j 个分数最大人数 = 前 j-2 个分数最大人数 + 第 j 个分数的人数
-
否。 most_p = most_p[n-1] 前 j 个分数最大人数 = 前 j-1 个分数最大人数
由此得dp状态转移方程:
d p [ i ] = m a x ( d p [ i − 2 ] + v a l [ i ] , d p [ i − 1 ] ) dp[i]=max(dp[i−2]+val[i],dp[i−1]) dp[i]=max(dp[i−2]+val[i],dp[i−1])
代码:
#include <iostream>
#include <vector>
using namespace std;
typedef long long ll;
const int maxp = 100005;
int score[maxp], N, K, max_score = -0x3f3f3f3f, temp, ans = 0, val[maxp], most_p[maxp];
vector<int> VS;
void dp()
{
//K==0不能分组需要单独列出
if (K == 0)
{
for (int i = 0; i <= max_score; i++)
if (score[i])
ans++;
}
//从0-(k-1)开始进行分组(是score%K的一系列值)。没必要用else,因为K=0进入不了分组循环
for (int i = 0; i < K; i++)
{
int cnt = 0;
// j,j+K,...,j+nK
for (int j = i; j <= max_score; j += K)
val[cnt++] = score[j];
//边界,dp出口
most_p[0] = val[0]; //前1个分数,没得选嘛
most_p[1] = max(val[0], val[1]); //前两个分数,选人数多的啦
//进行dp,对于前j个分数来说,考虑第j个分数选不选:
//1.选,那就是most_p[j - 2] + val[j],因为第j-1个分数不能选
//2.不选,那就和most_p[j - 1]一样
//所以,只需要取两种情况中最多的人数就可以啦
for (int j = 2; j < cnt; j++)
most_p[j] = max(most_p[j - 1], most_p[j - 2] + val[j]);
ans += most_p[--cnt];
}
}
int main()
{
cin >> N >> K;
while (N--)
{
cin >> temp;
score[temp]++;
max_score = max_score > temp ? max_score : temp;
}
dp();
cout << ans;
return 0;
}
参考文章: