藍橋杯 算法訓練 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

數據規模和約定

  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.糾結了幾天,把一個問題解決的感覺真爽!!!

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章