蓝桥杯 算法训练 Beaver's Calculator 归并排序 java 100分版

问题描述

  从万能词典来的聪明的海狸已经使我们惊讶了一次。他开发了一种新的计算器,他将此命名为"Beaver's Calculator 1.0"。它非常特别,并且被计划使用在各种各样的科学问题中。
  为了测试它,聪明的海狸邀请了n位科学家,编号从1到n。第i位科学家给这个计算器带来了 ki个计算题。第i个科学家带来的问题编号1到n,并且它们必须按照编号一个一个计算,因为对于每个问题的计算都必须依赖前一个问题的计算结果。
  每个教授的每个问题都用一个数 ai, j  来描述,i(1≤i≤n)是科学家的编号,j(1≤j≤ ki )是问题的编号, ai, j  表示解决这个问题所需资源单位的数量。
  这个计算器非常不凡。它一个接一个的解决问题。在一个问题解决后,并且在下一个问题被计算前,计算器分配或解放资源。
  计算器中最昂贵的操作是解放资源,解放远远慢于分配。所以对计算器而言,每一个接下来的问题所需的资源不少于前一个,是非常重要的。
  给你关于这些科学家所给问题的相关信息。你需要给这些问题安排一个顺序,使得“坏对”尽可能少。
  所谓“坏对”,就是相邻两个问题中,后一个问题需求的资源比前一个问题少。别忘了,对于同一个科学家给出的问题,计算它们的相对顺序必须是固定的。

输入格式

  第一行包含一个整数n,表示科学家的人数。接下来n行每行有5个整数,kiai, 1, xiyimi (0 ≤ ai, 1 < mi ≤ 109, 1 ≤ xi, yi ≤ 109) ,分别表示第i个科学家的问题个数,第1个问题所需资源单位数,以及3个用来计算 ai, j 的参量。ai, j = (ai, j - 1 * xi + yi)modmi。

输出格式

  第一行输出一个整数,表示最优顺序下最少的“坏对”个数。
  如果问题的总个数不超过200000,接下来输出  行,表示解决问题的最优顺序。每一行两个用空格隔开的整数,表示这个问题所需的资源单位数和提供这个问题的科学家的编号。

样例输入

2
2 1 1 1 10
2 3 1 1 10

样例输出

0
1 1
2 1
3 2
4 2

数据规模和约定

扫描二维码关注公众号,回复: 12064488 查看本文章

  20%的数据 n = 2, 1 ≤ ki ≤ 2000;
  另外30%的数据 n = 2, 1 ≤ ki ≤ 200000;
  剩下50%的数据 1 ≤ n ≤ 5000, 1 ≤ ki ≤ 5000。

解析:对每个科学家的问题:1.将遇到一个坏对的之前每一组问题和所有科学家遇到坏对之前的每一组问题(升序),合并起来,进行归并排序(稳定的排序方法不影响数组次序)02.重复第一步直到科学家的问题都被解决。

今天有点累了,直接放代码吧。不过也只有70分,不知道为什么没过,明天再看看,整理一下。(下面有正确代码)。

package March;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Question11 {
//Beaver's Calculator  

	final static int maxn=5100;
	static class t{
		int pos,count;//pos:当前的位置,count:某一个科学家的问题数
		long  p[]=new long [maxn+5];//科学家对应问题的资源数 从一开始
	}
	static t q[]=new t[maxn+5];//保存每个科学家的问题情况
	
	static class s{//安排的对应情况
		int id;//对应的科学家
		long  key;//需要的资源
	}
	static s R[]=new s[40*maxn+5];//安排
	static s R1[]=new s[40*maxn+5];
	
	static void init()//初始化
	{
		for(int i=0;i<maxn+5;i++)
			q[i]=new t();
		
		for(int i=0;i<40*maxn+5;i++)
		{
			R[i]=new s();
			R1[i]=new s();
		}	
	}
	
	static void Merge(int s,int m,int t)
	{
		int i,j,k;
		i=s;//R1第一个序列的始点
		j=m+1;//R1第二个序列的始点
		k=s;//R的始点 
		while(i<=m&&j<=t)
		{
			if(R1[i].key<R1[j].key)//从小到大
				R[k++]=R1[i++];
			else
				R[k++]=R1[j++];
		}
		while(i<=m) R[k++]=R1[i++];//如果R1第二个序列先排完
		while(j<=t) R[k++]=R1[j++];//如果R1第一个序列先排完
		
		for(i=s;i<k;i++)
			R1[i]=R[i];//将拍好序列的元素再赋值给R1
		
	}
	
	static void Msort(int s,int t)//二分法归并
	{
		if(s<t)
		{
			int m=(s+t)/2;
			Msort(s,m);
			Msort(m+1,t);
			Merge(s,m,t);
		}
	}
	
	public static void main(String[] args) throws IOException {
		int n,k,r,x,y,m;
		int length=0;
		int ans=-1;
		int start=0,end=0;
		
		BufferedReader bfr=new BufferedReader(new InputStreamReader(System.in));//加快输入
		String str=bfr.readLine();
		String S[]=str.split(" ");
		n=Integer.parseInt(S[0]);
		
		init();
		
		for(int i=1;i<=n;i++)
		{
			str=bfr.readLine();
			S=str.split(" ");
			k=Integer.parseInt(S[0]);
			r=Integer.parseInt(S[1]);
			x=Integer.parseInt(S[2]);
			y=Integer.parseInt(S[3]);
			m=Integer.parseInt(S[4]);
			int sum=0;
			length+=k;
			q[i].pos=1;//现在的位置
			q[i].count=k;//科学家i的问题总数
			q[i].p[1]=r;//第一个问题所需资源数
			for(int j=2;j<=k;j++)
			{
				q[i].p[j]=(x*q[i].p[j-1]+y)%m;
				if(q[i].p[j]<q[i].p[j-1])
					sum++;
			}
			ans=Math.max(ans, sum);
			//我们所需要求的最优顺序下最少的“坏对”个数,就是所有科学家所提出的问题的序列中的最多“坏对”的个数,即为所求。
		}
		System.out.println(ans);
		
		if(length<=200000)//依题意
		{
			while(end<length)//从0开始
			{
				for(int i=1;i<=n;i++)
				{
					int j;
					for( j=q[i].pos;j<=q[i].count;j++)
					{
						if(j!=q[i].pos&&q[i].p[j]<q[i].p[j-1])//发现坏对
						{
							q[i].pos=j;
							break;//跳出循环 破坏之  求的都是升序段
						}
						R1[end].id=i;
						R1[end++].key=q[i].p[j];
					}
					if(j>q[i].count)
						q[i].pos=j;//已完,科学家i的问题不在参与循环
				}
				Msort(start,end-1);//对所有科学家的升序段进行排序不会改变某一个科学家问题排列的相对位置。随便举个例子就OK啦,
				//end-1是因为 前面的是end++ 后自增,所以结束时,其大小要比数组最后一个有意义的值的序列值要大1
				start=end;//同理 ,开始下一段
			}
			for(int i=0;i<length;i++)
				System.out.println(R1[i].key+" "+R1[i].id);
		}
		
	}

}

评测详情:

详细记录
评测点序号 评测结果 得分 CPU使用 内存使用 下载评测数据
1 正确 5.00 640ms 244.3MB 输入 输出
2 正确 5.00 421ms 244.4MB VIP特权
3 正确 5.00 515ms 250.6MB VIP特权
4 正确 5.00 609ms 247.2MB VIP特权
5 运行错误 0.00 515ms 244.7MB VIP特权
6 运行错误 0.00 359ms 244.9MB VIP特权
7 运行错误 0.00 531ms 244.5MB VIP特权
8 运行错误 0.00 718ms 244.7MB VIP特权
9 运行错误 0.00 796ms 244.6MB VIP特权
10 运行错误 0.00 406ms 244.5MB VIP特权
11 正确 5.00 812ms 248.6MB VIP特权
12 正确 5.00 671ms 248.5MB VIP特权
13 正确 5.00 890ms 248.8MB VIP特权
14 正确 5.00 843ms 248.8MB VIP特权
15 正确 5.00 843ms 248.3MB VIP特权
16 正确 5.00 593ms 248.7MB VIP特权
17 正确 5.00 828ms 248.7MB VIP特权
18 正确 5.00 859ms 248.8MB VIP特权
19 正确 5.00 796ms 248.7MB VIP特权
20 正确 5.00 859ms 248.7MB VIP特权

想了好几天,都不知道错哪了,直到3月11号,突然一看发现 30%运行错误,和平时的错误是不一样的,那么肯定是其他的原因,百度了一下发现应该是数组越界,于是各种改,错误~~~,最后看数据规模

数据规模和约定

  20%的数据 n = 2, 1 ≤ ki ≤ 2000;
  另外30%的数据 n = 2, 1 ≤ ki ≤ 200000;
  剩下50%的数据 1 ≤ n ≤ 5000, 1 ≤ ki ≤ 5000。

第二个,ki最大可以200000,于是发现t出错了 因为t中p数组开到了5005,不错才怪呢。

之后,将p数组大小改为200005,本以为会过,but 内存超限,我r,再定睛一看,诶,n=2,嘿嘿嘿,特殊情况讨论,

if(n==2){} else{},一提交,ac,哈哈哈,

代码如下:



import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

//100points

public class Main {
//Beaver's Calculator  

	final static int maxn=5000;
	static class t{
		int pos,count;//pos:当前的位置,count:某一个科学家的问题数
		long  p[]=new long [maxn+5];//科学家对应问题的资源数 从一开始
	}
	static class T2{
		int pos,count;//pos:当前的位置,count:某一个科学家的问题数
		long  p[]=new long [200005];//科学家对应问题的资源数 从一开始
	}
	static t q[];//保存每个科学家的问题情况
	static T2 Q[];//n=2时  p[i] i=200005很大 会炸内存,必须先搞出来
	
	static class s{//安排的对应情况
		int id;//对应的科学家
		long  key;//需要的资源
	}
	static s R[]=new s[200005];//安排
	static s R1[]=new s[200005];
	
	static void init(int n)//初始化
	{
		if(n==2)//分类讨论
		{
			Q=new T2[3];	
			for(int i=1;i<=n;i++)//从1开始计数
				Q[i]=new T2();
		}
		else
		{
			q=new t[n+1];	
			for(int i=1;i<=n;i++)
				q[i]=new t();
		}
		
		for(int i=0;i<200005;i++)
		{
			R[i]=new s();
			R1[i]=new s();
		}	
	}
	
	static void Merge(int s,int m,int t)
	{
		int i,j,k;
		i=s;//R1第一个序列的始点
		j=m+1;//R1第二个序列的始点
		k=s;//R的始点 
		while(i<=m&&j<=t)
		{
			if(R1[i].key<R1[j].key)//从小到大
				R[k++]=R1[i++];
			else
				R[k++]=R1[j++];
		}
		while(i<=m) R[k++]=R1[i++];//如果R1第二个序列先排完
		while(j<=t) R[k++]=R1[j++];//如果R1第一个序列先排完
		
		for(i=s;i<k;i++)
			R1[i]=R[i];//将拍好序列的元素再赋值给R1
		
	}
	
	static void Msort(int s,int t)//二分法归并
	{
		if(s<t)
		{
			int m=(s+t)/2;
			Msort(s,m);
			Msort(m+1,t);
			Merge(s,m,t);
		}
	}
	
	public static void main(String[] args) throws IOException {
		int n,r,k,x,y,m;
		
		int length=0;
		int ans=-1;
		int start=0,end=0;
		
		BufferedReader bfr=new BufferedReader(new InputStreamReader(System.in));
//加快输入
		String str=bfr.readLine();
		String S[]=str.split(" ");
		n=Integer.parseInt(S[0]);
		
		init(n);
		
		for(int i=1;i<=n;i++)
		{
			str=bfr.readLine();
			S=str.split(" ");
			k=Integer.parseInt(S[0]);
			r=Integer.parseInt(S[1]);
			x=Integer.parseInt(S[2]);
			y=Integer.parseInt(S[3]);
			m=Integer.parseInt(S[4]);
			
			int sum=0;
			length+=k;
			if(n==2)
			{
				Q[i].pos=1;//现在的位置
				Q[i].count=k;//科学家i的问题总数
				Q[i].p[1]=r;//第一个问题所需资源数
				for(int j=2;j<=k;j++)
				{
					Q[i].p[j]=(Q[i].p[j-1]*x+y)%m;
					if(Q[i].p[j]<Q[i].p[j-1])
						sum++;
				}
			}
			else
			{
				q[i].pos=1;//现在的位置
				q[i].count=k;//科学家i的问题总数
				q[i].p[1]=r;//第一个问题所需资源数
				for(int j=2;j<=k;j++)
				{
					q[i].p[j]=(q[i].p[j-1]*x+y)%m;
					if(q[i].p[j]<q[i].p[j-1])
						sum++;
				}
			}
			
			ans=Math.max(ans, sum);
//我们所需要求的最优顺序下最少的“坏对”个数,就是所有科学家所提出的问题的序列中的最多“坏对”的个
//数,即为所求。
		}
		System.out.println(ans);
		
		if(length<=200000)//依题意
		{
			while(end<length)//从0开始
			{
				if(n==2)
				{
					for(int i=1;i<=n;i++)
					{
						int j;
						for( j=Q[i].pos;j<=Q[i].count;j++)
						{
							if(j!=Q[i].pos&&Q[i].p[j]<Q[i].p[j-1])//发现坏对
							{
								Q[i].pos=j;
								break;//跳出循环 破坏之  求的都是升序段
							}
							R1[end].id=i;
							R1[end++].key=Q[i].p[j];
						}
						if(j>Q[i].count)
							Q[i].pos=j;//已完,科学家i的问题不在参与循环
					}
				}
				else
				{
					for(int i=1;i<=n;i++)
					{
						int j;
						for( j=q[i].pos;j<=q[i].count;j++)
						{
							if(j!=q[i].pos&&q[i].p[j]<q[i].p[j-1])//发现坏对
							{
								q[i].pos=j;
								break;//跳出循环 破坏之  求的都是升序段
							}
							R1[end].id=i;
							R1[end++].key=q[i].p[j];
						}
						if(j>q[i].count)
							q[i].pos=j;//已完,科学家i的问题不在参与循环
					}
				}
			
				Msort(start,end-1);
//对所有科学家的升序段进行排序不会改变某一个科学家问题排列的相对位置。随便举个例子就OK啦,
//end-1是因为 前面的是end++ 后自增,所以结束时,其大小要比数组最后一个有意义的值的序列值要大1
				start=end;//同理 ,开始下一段
			}
			for(int i=0;i<length;i++)
				System.out.println(R1[i].key+" "+R1[i].id);
		}
		
	}

}

评测详情:

详细记录
评测点序号 评测结果 得分 CPU使用 内存使用 下载评测数据
1 正确 5.00 265ms 33.11MB 输入 输出
2 正确 5.00 171ms 33.13MB VIP特权
3 正确 5.00 484ms 42.58MB VIP特权
4 正确 5.00 390ms 41.54MB VIP特权
5 正确 5.00 218ms 33.98MB VIP特权
6 正确 5.00 156ms 33.98MB VIP特权
7 正确 5.00 250ms 33.86MB VIP特权
8 正确 5.00 218ms 34.06MB VIP特权
9 正确 5.00 203ms 33.98MB VIP特权
10 正确 5.00 234ms 33.92MB VIP特权
11 正确 5.00 750ms 236.1MB VIP特权
12 正确 5.00 921ms 235.9MB VIP特权
13 正确 5.00 765ms 235.6MB VIP特权
14 正确 5.00 687ms 236.1MB VIP特权
15 正确 5.00 812ms 235.6MB VIP特权
16 正确 5.00 703ms 235.9MB VIP特权
17 正确 5.00 781ms 235.7MB VIP特权
18 正确 5.00 656ms 235.6MB VIP特权
19 正确 5.00 843ms 235.8MB VIP特权
20 正确 5.00 796ms 236.0MB VIP特权

总结:

1.其实这道题也不是很难,就是一些数据会出现问题,下次改错的时候,一定要先辨别错误类型,之后再对症下药,不要瞎胡改,否则真的很浪费时间。(蓝桥杯运行错误一般是指数组越界)

2.遇到问题,要冷静分析,稳一点。

3.纠结了几天,把一个问题解决的感觉真爽!!!

猜你喜欢

转载自blog.csdn.net/Look_star/article/details/88262410
今日推荐