1. 程式人生 > >2018/11/02 ACM集訓第三次周賽題解及自身題目優化

2018/11/02 ACM集訓第三次周賽題解及自身題目優化

問題 A: 珠心算測試 題目描述 珠心算是一種通過在腦中模擬算盤變化來完成快速運算的一種計算技術。珠心算訓練, 既能夠開發智力,又能夠為日常生活帶來很多便利,因而在很多學校得到普及。 某學校的珠心算老師採用一種快速考察珠心算加法能力的測驗方法。他隨機生成一個正 整數集合,集合中的數各不相同,然後要求學生回答:其中有多少個數,恰好等於集合中另 外兩個(不同的)數之和? 最近老師出了一些測驗題,請你幫忙求出答案。

輸入 輸入共兩行,第一行包含一個整數 n,表示測試題中給出的正整數個數。 第二行有 n 個正整數,每兩個正整數之間用一個空格隔開,表示測試題中給出的正整數。

輸出 輸出共一行,包含一個整數,表示測驗題答案。

樣例輸入

4
1 2 3 4

樣例輸出

2

提示 【樣例說明】 由 1+2=3,1+3=4,故滿足測試要求的答案為 2。注意,加數和被加數必須是集合中的 兩個不同的數。

【資料說明】 對於 100%的資料,3 ≤ n ≤ 100,測驗題給出的正整數大小不超過 10,000。

分析:這個題中有的陷阱在於,一個數有可能會是集合中兩組數的和,如1,2,3,4,5中1+4=5,2+3=5,這個地方錯了兩遍,都是因為在這兒要不是多減就是少減了。藍受香菇。இ௰இ最後是用了另一個數組來儲存已經出現過一次的數,從而避免重複。

#include<bits/stdc++.h>
using namespace std;
int main() { int n,a[105]; scanf("%d",&n); int i; for(i=0;i<n;i++) { scanf("%d",&a[i]); } int item=0; int b[100]; int x=0; for(i=0;i<n-1;i++) for(int k=i+1;k<n;k++) { for(int t=0;t<n;t++) { if(a[i]+a[k]==a[t]) { if(b[t]==a[t]) item--; b[t]=a[t]; item++
; break; } } } printf("%d",item); return 0; }

問題B:比例簡化 題目描述 在社交媒體上,經常會看到針對某一個觀點同意與否的民意調查以及結果。例如,對某 一觀點表示支援的有 1498 人,反對的有 902 人,那麼贊同與反對的比例可以簡單的記為 1498:902。 不過,如果把調查結果就以這種方式呈現出來,大多數人肯定不會滿意。因為這個比例 的數值太大,難以一眼看出它們的關係。對於上面這個例子,如果把比例記為 5:3,雖然與 真實結果有一定的誤差,但依然能夠較為準確地反映調查結果,同時也顯得比較直觀。 現給出支援人數 A,反對人數 B,以及一個上限 L,請你將 A 比 B 化簡為 A’比 B’,要 求在 A’和 B’均不大於 L 且 A’和 B’互質(兩個整數的最大公約數是 1)的 前提下,A’/B’ ≥ A/B 且 A’/B’ - A/B 的值儘可能小。

輸入 輸入共一行,包含三個整數 A,B,L,每兩個整數之間用一個空格隔開,分別表示支援 人數、反對人數以及上限。

輸出 輸出共一行,包含兩個整數 A’,B’,中間用一個空格隔開,表示化簡後的比例。

樣例輸入

1498 902 10 

樣例輸出

5 3

提示 【資料說明】 對於 100%的資料,1 ≤ A ≤ 1,000,000,1 ≤ B ≤ 1,000,000,1 ≤ L ≤ 100, A/B ≤ L。

分析: 這個題目沒做粗來,很痛苦,,後來看了題解後發現,並沒有特別地難,首先要求在 A’和 B’均不大於 L 且 A’和 B’互質(兩個整數的最大公約數是 1)的前提下,所以先要考慮已給出的資料是否滿足這一情況,所以要求粗兩個資料的最大公約數。 到這裡,看了林姐的題解後,學到了通過遞迴函式來求gcd的方法。

int gcd(int a,int b)
{
	return b==0?a:gcd(b,a%b);
}

如果給出的資料不滿足條件,因為資料比較小,所以可以直接暴力求解。利用兩個迴圈來求滿足條件的數,A’/B’ ≥ A/B 且 A’/B’ - A/B 的值儘可能小,可將式子轉換為A’B ≥ AB’,A’/B’ - A/B 的值儘可能小,也可用上面的公式。

#include<bits/stdc++.h>
using namespace std;
int gcd(int a,int b)
{
	return b==0?a:gcd(b,a%b);
}
int main()
{
	int A,B;
	int g;
	int L;
	scanf("%d%d%d",&A,&B,&L);
	int x=A,y=B;
	g=gcd(A,B);
	if(A/g<=L&&B/g<=L)
	{
		printf("%d %d",A/g,B/g);
		return 0;
	}
	else
	{
		int x=1000001,y=1;
		int i,k;
		for(i=1;i<=L;i++)
		{
			for(k=1;k<=L;k++)
			{
				if(i*B>=k*A)
				{
					if(i*y<k*x)
					{
						x=i;
						y=k;
					}
					
				}
			}
		}
		printf("%d %d",x,y);
	}

	return 0;
}

問題C:螺旋矩陣 題目描述 一個 n 行 n 列的螺旋矩陣可由如下方法生成: 從矩陣的左上角(第 1 行第 1 列)出發,初始時向右移動;如果前方是未曾經過的格子, 則繼續前進,否則右轉;重複上述操作直至經過矩陣中所有格子。 根據經過順序,在格子中 依次填入 1, 2, 3, … , n 2 ,便構成了一個螺旋矩陣。 下圖是一個 n = 4 時的螺旋矩陣。

現給出矩陣大小 n 以及 i 和 j,請你求出該矩陣中第 i 行第 j 列的數是多少。

輸入 輸入共一行,包含三個整數 n,i,j,每兩個整數之間用一個空格隔開,分別表示矩陣 大小、待求的數所在的行號和列號。

輸出 輸出共一行,包含一個整數,表示相應矩陣中第 i 行第 j 列的數。

樣例輸入

4 2 3

樣例輸出

14

提示 【資料說明】 對於 50%的資料,1 ≤ n ≤ 100; 對於 100%的資料,1 ≤ n ≤ 30,000,1 ≤ i ≤ n,1 ≤ j ≤ n。

分析:在這個題中,我將每個矩陣的一圈的數字寫成了一個函式,並利用函式的自身呼叫來實現矩陣內層的數計算,直至矩陣座標為輸入時的要求停止。我將矩陣的列設為x,行設為y,按照順時針的順序進行座標變化和數自增1.轉完一圈後為函式執行一遍,然後用以n-1的圈為單位,2,2座標開始執行。過程比較麻煩,可能還有更簡便的。

#include<bits/stdc++.h>
using namespace std;
int num=1;
int x=1,y=1,k=1;
void NUM(int a,int b,int c,int k)
{
   if(x==c&&y==b)
   {
   	printf("%d",num);
   	return;
   }
   while(x<a)
   {
   		if(x==c&&y==b)
   	{
   		printf("%d",num);
   		return;
   	}
   	x++;
   	num++;
   }
   while(y<a)
   {
   		if(x==c&&y==b)
   	{
   		printf("%d",num);
   		return;
   	}
   	y++;
   	num++;
   }
   while(x>k)
   {
   		if(x==c&&y==b)
   	{
   		printf("%d",num);
   		return;
   	}
   	x--;
   	num++;
   }
   while(y>k)
   {
   		if(x== c&&y == b)
   	{
   		printf("%d",num);
   		return;
   	}
   	y--;
   	num++;
   	if(y == k&&x ==k)
   	{
   		x++;
   		y++;
   		k++;
   	}
   }
   NUM(a-1,b,c,k);
   
}
int main()
{
   int a[100][100];
   int n,i,j;
   scanf("%d%d%d",&n,&i,&j);
   int x,y;
   NUM(n,i,j,k);
   return 0;
}

問題D:子矩陣(待解決) 題目描述 給出如下定義: 1. 子矩陣:從一個矩陣當中選取某些行和某些列交叉位置所組成的新矩陣(保持行與 列的相對順序)被稱為原矩陣的一個子矩陣。 例如,下面左圖中選取第 2、4 行和第 2、4、5 列交叉位置的元素得到一個 2*3 的子矩 陣如右圖所示。 在這裡插入圖片描述 2. 相鄰的元素:矩陣中的某個元素與其上下左右四個元素(如果存在的話)是相鄰的。 3. 矩陣的分值:矩陣中每一對相鄰元素之差的絕對值之和。 本題任務:給定一個 n 行 m 列的正整數矩陣,請你從這個矩陣中選出一個 r 行 c 列的 子矩陣,使得這個子矩陣的分值最小,並輸出這個分值。

輸入 第一行包含用空格隔開的四個整數 n,m,r,c,意義如問題᧿述中所述,每兩個整數 之間用一個空格隔開。 接下來的 n 行,每行包含 m 個用空格隔開的整數,用來表示問題描述中那個 n 行 m 列 的矩陣。

輸出 輸出共 1 行,包含 1 個整數,表示滿足題目描述的子矩陣的最小分值。

樣例輸入 5 5 2 3 9 3 3 3 9 9 4 8 7 4 1 7 4 6 6 6 8 5 6 9 7 4 5 6 1

樣例輸出 6

提示 【輸入輸出樣例 說明】 該矩陣中分值最小的 2 行 3 列的子矩陣由原矩陣的第 4 行、第 5 行與第 1 列、第 3 列、第 4 列交叉位置的元素組成, 為 6 5 6 7 5 6 其分值為 6 − 5 + 5 − 6 + 7 − 5 + 5 − 6 + 6 − 7 + 5 − 5 + 6 − 6 = 6。

問題E:計數問題 題目描述 試計算在區間 1 到 n 的所有整數中,數字 x(0 ≤ x ≤ 9)共出現了多少次?例如,在 1 到 11 中,即在 1、2、3、4、5、6、7、8、9、10、11 中,數字 1 出現了 4 次。

輸入 輸入共 1 行,包含 2 個整數 n、x,之間用一個空格隔開。

輸出 輸出共 1 行,包含一個整數,表示 x 出現的次數。

樣例輸入 11 1

樣例輸出 4

提示 【資料說明】 對於 100%的資料,1≤ n ≤ 1,000,000,0 ≤ x ≤ 9。 分析:直接將數的各個位拆開,出現一個要求統計的數字就計數一次。

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int n,x;
	int i,item=0;
	int a;
	scanf("%d%d",&n,&x);
	for(i=1;i<=n;i++)
	{
		a=i;
		while(a!=0)
		{
			if(x==a%10)
			item++;
			a=a/10;
		}
	}
	printf("%d",item);
	
	return 0;
}

問題F:表示式求值 題目描述 給定一個只包含加法和乘法的算術表示式,請你程式設計計算表示式的值。

輸入 輸入僅有一行,為需要你計算的表示式,表示式中只包含數字、加法運算子“+”和乘 法運算子“*”,且沒有括號,所有參與運算的數字均為 0 到 2^31 -1 之間的整數。 輸入資料保 證這一行只有 0~ 9、+、*這 12 種字元。

輸出 輸出只有一行,包含一個整數,表示這個表示式的值。 注意:當答案長度多於 4 位時, 請只輸出最後 4 位,前導 0 不輸出。

樣例輸入 1+1*3+4

樣例輸出 8

提示 樣例 計算的結果為 8,直接輸出 8。

【資料範圍】 對於 30%的資料,0≤表示式中加法運算子和乘法運算子的總數≤100; 對於 80%的資料,0≤表示式中加法運算子和乘法運算子的總數≤1000; 對於 100%的資料,0≤表示式中加法運算子和乘法運算子的總數≤100000。 分析:這個題沒有做出來,因為錯在沒有想到如何做到先乘後加的實現方法,,菜啊?,後來結束後就像出來了,輸入一個數後,再輸入一個字元,判斷字元的型別如果是‘\n’,結束程式,如果是乘則將前面的一個元素與下一個元素相乘直至出現加號,而相乘後前面的那個元素就可以變為0;最後把陣列中所有元素相加。

#include<bits/stdc++.h>
using namespace std;
int main()
{
	long long a[100005];
	char b;
	int i=0;
	scanf("%lld",&a[i]);
	a[i]=a[i]%10000;
	i++;
	while(1)
	{
		b=getchar();
		if(b=='\n')
			break;
		if(b=='*')
		{
			scanf("%lld",&a[i]);
			a[i]%=10000;
			a[i]=a[i-1]*a[i]%10000;//所有元素相乘
			a[i-1]=0;//前一個元素為0
			i++;
		}
		if(b=='+')
		{
			scanf("%lld",&a[i]);
			a[i]=a[i]%10000;
			i++;
		}
	}
	long long sum=0;
	for(int k=0;k<i;k++)
	{
		sum=(sum+a[k])%10000;
	}
	printf("%lld",sum);
	return 0;
 }