(第八届蓝桥杯国赛)对局匹配(动态规划)

题目链接:“蓝桥杯”练习系统

样例输入:

10 0
1 4 2 8 5 7 1 4 2 8

样例输出:

6

分析:这道题目我们可以分情况进行讨论,先看一下k=0的情况,也就是不能选取相同分数的人,那么很容易想到有多少种不同的积分,那么答案就是几,下面我重点分析一下k!=0的情况,我们可以对所有的分数进行分组,分组规则就是用分数对k取余所得的余数相同的为一组,这样我们可以分为k组,我们可以发现一个性质就是不同组之间的积分是不可能相差k的,也就是说积分相差为k的人一定在同一组,且组与组之间是相互独立的,那么我们可以分别求出每一组内最多能选取的数目,然后相加即为答案。那怎样求出来每一组内最多能选取的数目呢?我们发现在求每一组内最多能选取的数目时我们可以类比没有上司的舞会那道题目,用f[i][0]表示没有选取分数为i的人时所能选取的最大人数,f[i][1]表示选取分数为i的人时所能选取的最大人数,当我们要选取分数为i的人,肯定是选取所有的分数为i的人,这是利用贪心性质得到的,也是很容易想明白的。下面我们来看一下如何动态转移,先对每一组内的分数进行从小到大排序,比如k=3时,一组内的分数为1,4,7,10,……3*(count-1)+1共count个数,当然每个分数对应的人数可以为0,考虑4这个分数的人,假如我们选取了4这个分数的人,那么一定就不能选取1这个分数的人(我们是从前往后递推的,所以这个时候不需要考虑7),如果我们不选4这个分数的人,那么我们可以选择1这个分数对应的人,也可以不选,只需要从两者取一个最大值即可。按照这个思路,更新完组内所有的数就可以得到该组能够选取的最多的人数为max(f[count][1],f[count][0]),然后类似地求出其他组,把每一组得到的最大值相加即为答案。

下面是代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1e5+10;
int f[N][2];//f[i][0/1]表示不选/选组内第i小分数所能选取的最多人数 
int cnt[N];//cnt[i]记录i这个分数对应的人数 
int a[N];//a[i]记录组内第i小分数所对应的人数 
int main()
{
	int n,k;
	cin>>n>>k;
	int t,ans=0,mx=0;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&t);
		cnt[t]++;
	}
	if(k==0)
	{
		for(int i=0;i<=100000;i++)
			if(cnt[i]) ans++;
	}
	else 
	for(int i=0;i<k;i++)
	{
		int count=0;
		for(int j=i;j<=100000;j+=k)
			a[++count]=cnt[j];
		for(int j=1;j<=count;j++)
		{
			f[j][0]=max(f[j-1][0],f[j-1][1]);
			f[j][1]=f[j-1][0]+a[j];
		}
		ans+=max(f[count][0],f[count][1]);//记录第i组中最多能选出来的个数 
	}
	cout<<ans;
	return 0;
} 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值