[蓝桥杯][历届试题 PREV-50]对局匹配(Java)(动态规划)

 

 历届试题 对局匹配  

时间限制: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


解题思路:

一般看到这种需要求最优解或者最优方案之类的题,一部分与动态规划有关(也不全是),

首先我们要对所有数据统计,每个数出现多少次。

对于所有数据求动态公式是一件难事,所以先要分组(k为0时除外),将连续相差为k的值分到一组,求组中的最大解:

       例如:k为3,那么第一组的数据有,0,3,6,9,12……

                            那么第二组的数据有,1,4,7,10,13……

                            那么第三组的数据有,2,5,8,11,14……

然后将每组中求得的最优解相加,答案就是所有数据的最优解。

================更新===============

因为数据的最大为100000,所以创建int[] val = new int[100000+1];,这样可以统计重复值的个数,也方便计算相差k时的数值。

反思错误:

①看到题想到动态规划,但是没有想到分组考虑,实现难度很大

②分组考虑后,以自认为简化的程序实现,结果超时。后来分析循环一百万次的赋值都比循环十万次的Math.max时间短

③忘记考虑有连续重复值情况(n=5,k=5,值为1,4,4,4,9)。

Java代码:

import java.util.Scanner;

public class Main {
	public static void main(String[] args) {
		int res = 0;
		Scanner cin = new Scanner(System.in);
		int N = cin.nextInt();
		int k = cin.nextInt();
		int[] val = new int[100001];
		//如果k不等于0,
		for(int i=0;i<N;i++) {
			val[cin.nextInt()]++;
		}
		if(k==0) {
			for(int i=0;i<100001;i++) {
				if(val[i]!=0) res++;
			}
		}
		else {
			int[] agroup = new int[100001];//一个组,每次循环,重新利用这个组
			int[] dp = new int[100001];
			//一共有k个组,循环每组,找出能过匹配的最大的人数
			for(int i=0;i<k;i++) {
				int len = 1;
				for(int j=i;j<100001;j+=k) {
					agroup[len++] = val[j];//当前组的第len个值为val[j]
				}
				
				dp[0] = 0;dp[1] = agroup[1];
				for(int j=2;j<len;j++) {
					dp[j] = Math.max(dp[j-1], dp[j-2]+agroup[j]);
				}
				res += dp[len-1];
			}
		}
		System.out.println(res);
		cin.close();
	}
}

测试结果截图:

================二次提交==========================

java代码(快读,核心代码在类SO中的sol函数):

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.StringTokenizer;


public class Main{
	public static void main(String[] args) {
//		long start = System.currentTimeMillis();
        
        
		InputStream is = System.in;
		OutputStream os = System.out;
		
		IN cin = new IN(is);
		PrintWriter cout = new PrintWriter(os);
		
		SO so = new SO();
		so.sol(cin,cout);
		
		cout.flush();
		
//		long end = System.currentTimeMillis();
//        System.out.println("using time:" + (end - start) + " ms");
	}
	
	static class SO{
		static final int MAX = 100000;
		void sol(IN cin,PrintWriter cout) {
			int n = cin.nextInt(), k = cin.nextInt();
			int[] nums = new int[MAX+1];
			int res = 0;
			//get nums
			for(int i=0;i<n;++i)
				++nums[cin.nextInt()];
			//k==0 
			if(k==0) {
				for(int i:nums)
					res+=i==0?0:1;
				cout.println(res);
				return;
			}
			//
			int[] dp = new int[MAX+1];
			for(int i=0;i<k;++i) {
				int top = 1;
				if(i<=MAX)dp[top++]=nums[i];//dp[1]
				for(int j=i+k;j<=MAX;j+=k) {
					dp[top] = Math.max(dp[top-1], dp[top-2]+nums[j]);
					++top;
				}
				res += dp[top-1];
			}
			cout.println(res);
			
		}//end sol
		
		
		
	}//end SO
	
	public static class IN{
		private BufferedReader reader;
		private StringTokenizer tokenizer;
		
		public IN(InputStream is) {
			reader = new BufferedReader(new InputStreamReader(is),32768);
			tokenizer = null;
		}
		
		public String next() {
			while(tokenizer==null || !tokenizer.hasMoreTokens()) {
				try {
					tokenizer = new StringTokenizer(reader.readLine());
				} catch (IOException e) {
					throw new RuntimeException(e);
				}
			}
			return tokenizer.nextToken();
		}
		
		public int nextInt() {
			return Integer.parseInt(next());
		}
	}//end IN
}

结果截图:

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值