蓝桥杯 历届试题 对局匹配

探讨一个围棋网站如何在用户间进行棋力匹配的问题,通过动态规划算法解决最多可能有多少用户因找不到符合条件的对手而无法对局。

版权声明:本文为博主原创文章,未经博主允许不得转载。

						  历届试题 对局匹配  
						时间限制: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

原文摘自蓝桥杯。

看到这数据就猜是dp了。
思路解析:
假设k=1;
对于num[0],num[1],有两种情况,第一 选num[0],不选num[1]。即dp[0][0]=num[0],dp[1][0]=0,第二选num[1],不选num[0]。dp[0][1]=0,dp[1][1]=num[1]。
则对于num[2]选还是不选就由前面num[0],num[1]决定。
dp[2][0]=max(dp[1][0],dp[0][0]+num[2]]),dp[2][1]=max(dp[1][1],dp[0][1]+num[2]);
同理可以得出k!=0的所有状态转移方程dp[i][0]=max(dp[i-k][0],dp[i-2k][0]+num[i]]),dp[i][1]=max(dp[i-k][1],dp[i-2k][1]+num[i]);其中(i>=k*2)
假设x为初始的起点;
因为对于0<=x<k,x对应不同的初始值直接是不会互相影响的
令ans[i%m]=max(dp[i][0],dp[i][1]);可以得出不同初始值所对应的最大可选数量。
则答案res就是ans[0]+ans[1]+…+ans[k-1];
由于这种dp的状态转移方程只适用k!=0,因此对于k==0得特殊判断
复杂度O(n)。

#include<iostream>
using namespace std;
int num[100005],ans[100005];
int dp[100005][2];
int get_Max(int a,int b)求最大值
{
	if(a>b)
		return a;
	return b;
}
int main()
{
	int i,j,n,m;	
	int res=0;
	scanf("%d %d",&n,&m);
	for(i=0;i<n;i++)
	{
		int x;
		scanf("%d",&x);	
		if(m==0)
		{
			if(!num[x])
			{
				res++;
			}else
			{
				continue;
			}
		}
		num[x]++;
	}
	if(m==0)
	{
		printf("%d\n",res);
		return 0;
	}
	for(i=0;i<m;i++)//第一种情况
	{
		dp[i][0]=num[i];
		ans[i]=get_Max(num[i],ans[i]);
	}
	for(i=m;i<m*2;i++)//第二种情况
	{
		dp[i][1]=num[i];
		ans[i%m]=get_Max(num[i],ans[i%m]);
	}
	for(i=m*2;i<=100000;i++)
	{
		dp[i][0]=get_Max(dp[i-m*2][0]+num[i],dp[i-m][0]);//状态转移方程
		dp[i][1]=get_Max(dp[i-m*2][1]+num[i],dp[i-m][1]);
		ans[i%m]=get_Max(dp[i][1],ans[i%m]);//保存过程中不同初始值最大可选人数
		ans[i%m]=get_Max(dp[i][0],ans[i%m]);
	}

	for(i=0;i<m;i++)//求结果
	{
		res+=ans[i];
	}
	printf("%d\n",res);
	return 0;	
}//代码仅是草创,仅与借鉴

博主第一处写博客,若有瑕疵,愿请谅解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值