視覺化JVM中的記憶體管理
實驗室第一次排位賽習題分析
A-sky數:
熊熊學長髮現了一個有趣的四位數2992,這個數,它的十進位制數表示,其四位數字之和為2+9+9+2=22,它的十六進位制數BB0,其四位數字之和也為22,同時它的十二進位制數表示1894,其四位數字之和也為22,啊哈,真是巧啊。熊熊學長非常喜歡這種四位數,由於他的發現,所以這裡我們命名其為Sky數。但是要判斷這樣的數還是有點麻煩啊,那麼現在請你幫忙來判斷任何一個十進位制的四位數,是不是Sky數吧。
Input
輸入含有一些四位正整數,如果為0,則輸入結束。
Output
若n為Sky數,則輸出“#n is a Sky Number.”,否則輸出“#n is not a Sky Number.”。每個結果佔一行。注意:#n表示所讀入的n值。
Sample Input
2992
1234
0
Sample Output
2992 is a Sky Number.
1234 is not a Sky Number.
#include<stdio.h>
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
if(n==0)
break;
int sum1=0,sum2=0,sum3=0;
int b=n;
while(b)
{
sum1+=b%10;
b=b/10;
}
b=n;
while(b)
{
sum2+=b%12;
b=b/12 ;
}
b=n;
while(b)
{
sum3+=b%16;
b=b/16;
}
if(sum1==sum2&&sum1==sum3)
printf("%d is a Sky Number.\n",n);
else
printf("%d is not a Sky Number.\n",n);
}
return 0;
}
- 此題我將每個進位制都分別寫出,暴力求解
- 優化方法:利用函式代替單一重複,使用時傳參即可
B-哥德巴赫猜想:
倩倩學姐想把一個偶數拆成兩個不同素數的和,你有有幾種拆法呢?
Input
輸入包含一些正的偶數,其值不會超過10000,個數不會超過500,若遇0,則結束。
Output
對應每個偶數,輸出其拆成不同素數的個數,每個結果佔一行。
Sample Input
30
26
0
Sample Output
3
2
#include<stdio.h>
int main()
{
int a[10001],n;
for(int i=2;i<=10000;i++)
{
a[i]=i;
}
for(int i=2;i<=5000;i++)
{
for(int j=2*i;j<=10000;j+=i)
{
a[j]=0;
}
}
while(scanf("%d",&n)!=EOF)
{
if(n==0)
break;
int m=0;
for(int i=3;2*i<n;i++)
{
if(a[i]+a[n-i]==n)
++m;
}
printf("%d\n",m);
}
return 0;
}
比賽時問題出在:
- int a[10001]定義成a[10000]導致資料儲存不全出現WA
- 剛開始利用迴圈巢狀,時間複雜度較高出現TLE
解題思路與收穫:
- 使用製表方法將10000以內的素數統計出來,不是素數的記為0,這樣後續加法不會造成影響。為了提高製表速率,我們知道合數必定是一個數的整數倍(除了1和他本身)所以直接將2到5000的所有值的倍數剔除即可。
- 素數判斷優化方法
C-這是什麼呀:
現在我們有兩個正整數A和B,請找出一個正整數C,使得式子( (A xor C)&(B xor C) )最小 (xor是異或運算)當然,如果使得式子最小的C為0時,請你輸出1
Input
第一行輸入T,代表有T組資料, 接下來每一行輸入兩個數A,B(A,B<=2^32)
Output
對於每一組資料輸出一個正整數C
Sample Input
1
3 5
Sample Output
1
#include<stdio.h>
int main()
{
int t;
scanf("%d",&t);
long long a,b;
while(t--)
{
scanf("%lld%lld",&a,&b);
long long c=a&b;
printf("%lld\n",c?c:1);
}
return 0;
}
解題思路與收穫:
- 瞭解xor是異或運算,&是與運算,以及其性質
2.可以列出幾個值之間的關係表格觀察,表格在文末PPT裡有,需要可以前往檢視
D-倩姐的自我突破 :
倩倩學姐,一個大三的老阿姨呢,但是呢她還有一顆堅持競賽的心。所以他碰到一個奧林匹克競賽的數學問題,她搞不定很難受,決定暴飲暴食。聰明而優秀的學霸熊熊學長看到這一幕決定幫他解決這個問題。這個問題是:
我們描述 K!
k! = 1 * 2 * …* (k - 1) k
我們表示 S:
S = 1 * 1! + 2 * 2! + … +(n - 1) * (n-1)!
然後 S 對 n 去模是 ___________
你將得到一個整數n.
你需要計算 S 對 n 取模的值
輸入
第一行輸入一個整數 T(T < 1000), 表示測試用例的行數.
對於每個測試用例,都有一行包含一個整數 n.
輸出
對於每個測試用例,列印一個整數 S 對 n 取模後的值.
提示
第一個測試用例 S = 1 1!= 1, 並且 1 的模 2 運算 1.
第二個測試用例 S = 11!+2 2!= 5 , 並且 5 對 3 取模是 2.
Sample Input
2
2
3
Sample Output
1
2
#include<stdio.h>
int main()
{
long long t;
scanf("%lld",&t);
while(t--)
{
long long n;
scanf("%lld",&n);
printf("%lld\n",n-1);
}
return 0;
}
解題思路與收穫:
- 首先這個題程式碼很短,沒錯,就是這麼短小精悍,直接輸入一個值,輸出這個值-1就行。我嘗試用迴圈,每次都超時,誰能想到這樣都行。
- 所以學到了,找規律也很重要!下面是規律圖
輸入 | 輸出 |
---|---|
2 | 1 |
3 | 2 |
4 | 3 |
5 | 4 |
… | … |
n | n-1 |
E-親和數 :
古希臘數學家畢達哥拉斯在自然數研究中發現,220的所有真約數(即不是自身的約數)之和為:
1+2+4+5+10+11+20+22+44+55+110=284。而284的所有真約數為1、2、4、71、 142,加起來恰好為220。人們對這樣的數感到很驚奇,並稱之為親和數。一般地講,如果兩個數中任何一個數都是另一個數的真約數之和,則這兩個數就是親和數。
你的任務就編寫一個程式,判斷給定的兩個數是否是親和數
Input
輸入資料第一行包含一個數M,接下有M行,每行一個例項,包含兩個整數A,B; 其中 0 <= A,B <= 600000 ;
Output
對於每個測試例項,如果A和B是親和數的話輸出YES,否則輸出NO。
Sample Input
2
220 284
100 200
Sample Output
YES
NO
#include<stdio.h>
int main()
{
int M;
scanf("%d",&M);
while(M--)
{
int A,B,sum1=0,sum2=0;
scanf("%d%d",&A,&B);
for(int i=1;i<=A/2;i++)
{
if(A%i==0)
sum1+=i;
}
if(sum1==B)
{
for(int i=1;i<=B/2;i++)
{
if(B%i==0)
sum2+=i;
}
if(sum2==A)
printf("YES\n");
else
printf("NO\n");
}
else
printf("NO\n");
}
return 0;
}
簡單就不過多贅述了。
F- 熊熊的嘗試 :
熊熊學長一天在實驗室裡閒的沒事。他想做點遊戲打發一下時間。他就拉上了和他同樣無聊的柴柴學長。
兩位學長要玩的遊戲是什麼呢?很簡單,它是這樣定義的:
1、 本遊戲是一個二人遊戲;
2、 有一堆石子一共有n個;
3、 兩人輪流進行;
4、 每走一步可以取走1…m個石子;
5、 最先取光石子的一方為勝;
如果遊戲的雙方使用的都是最優策略,請輸出哪個人能贏。
Input
輸入資料首先包含一個正整數C(C<=100),表示有C組測試資料。每組測試資料佔一行,包含兩個整數n和m(1<=n,m<=1000),n和m的含義見題目描述。
Output
如果先走的人能贏,請輸出“first”,否則請輸出“second”,每個例項的輸出佔一行。
Sample Input
2
23 2
4 3
Sample Output
first
second
#include<stdio.h>
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int a,b;
scanf("%d%d",&a,&b);
if(a%(b+1)==0)
printf("second\n");
else
printf("first\n");
}
return 0;
}
解題思路與收穫:
- 這個程式碼也很簡短,主要涉及到博弈論,有先手必勝態和後手必勝態(雙方都想要贏,不要想什麼放水)
- 當剩餘m+1時,無論先手怎麼取,都是後手勝。
- 所以當剛開始的數量等於m+1的整數倍時,在先手取完後,後手都可以讓其重新恢復m+1的整數倍,所以後手必勝。
- 若剛開始不是m+1的整數倍,那麼先手總可以讓其變為m+1的整數倍,所以先手必勝。
G-數塔問題:
有如下所示的數塔,要求從頂層走到底層,若每一步只能走到相鄰的結點,則經過的結點的數字之和最大是多少?
圖片:
Input
輸入資料首先包括一個整數C,表示測試例項的個數,每個測試例項的第一行是一個整數N(1 <= N <= 100),表示數塔的高度,接下來用N行數字表示數塔,其中第i行有個i個整數,且所有的整數均在區間[0,99]內。
Output
對於每個測試例項,輸出可能得到的最大和,每個例項的輸出佔一行。
Sample Input
1
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
Sample Output
30
#include<stdio.h>
int main()
{
int C;
scanf("%d",&C);
while(C--)
{
int N;
scanf("%d",&N);
int a[100][100];
for(int i=0;i<N;i++)
{
for(int j=0;j<=i;j++)
{
scanf("%d",&a[i][j]);
}
}
for(int i=N-1;i>0;i--)
{
for(int j=0;j<N-1;j++)
{
a[i-1][N+j]=(a[i][j]>a[i][j+1]?a[i][j]:a[i][j+1])+a[i-1][j];
a[i-1][j]=a[i-1][N+j];
}
}
printf("%d\n",a[0][0]);
}
return 0;
}
解題思路與收穫:
- 從下到上取分支的兩個數中的最大值加到共同連線的上一層,此時上一層的新值為最優解,以此類推,最下層判斷完後,此時上一層都為各自路徑的最優解,再以同樣方法向上走,最終取得全域性最優解。
- 合理利用二維陣列
數塔詳解請點選這裡
H-Tyloo的S1mple本人 :
熊熊是一名csgo玩家,在沙二馳騁多年的他,顯然已經對這個地方瞭如指掌。他恐怖的定位和風騷的身法以及高超的戰術讓他的隊友後悔來到這個地方。尤其是他的大狙,當他扛著笨重的awp走到中門時,每一聲槍響都會有人應聲倒地。但是熊熊有個奇怪的癖好,他每一次只殺奇數個數的人,一殺他嫌太少,九殺又太多,所以他每一局他只會打出三殺,五殺或七殺。現在你在他旁邊看他打遊戲,你看到他殺了n個人,你現在想知道他分別打出了多少個三殺,五殺或七殺。
Input
第一行的整數 t(1<=t<=1000)— 測試用例的個數.每個測試用例只有一個輸入資料 — lrh殺人的總數 n(1<=n<=1000)
Output
如果對於某個測試樣例,沒有正確的答案,則輸出 -1.否則,輸出3個正整數-三殺的個數,五殺的個數,七殺的個數 — 如果存在多個情況,輸出任意一種即可
Input
4
30
67
4
14
Output
2 2 2
7 5 3
-1
0 0 2
#include<stdio.h>
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,sum;
scanf("%d",&n);
for(int i=0;i<=n;i+=7)
{
for(int j=0;j<=n;j+=5)
{
sum=i+j;
if(sum>n)
break;
if((n-sum)%3==0)
{
printf("%d %d %d\n",(n-sum)/3,j/5,i/7);
goto ABC;
}
}
}
printf("-1\n");
ABC:;
}
return 0;
}
解題思路與收穫:
- 這個題有多個答案,題中說只需任意一組即可,故只要滿足3a+5b+7*c=n即可
- 利用迴圈巢狀,如果滿足,直接輸出並跳到下個數據輸入。
J-最後是啥呢:
每次zwt可以選擇兩個不同的數字,並且大喊一聲“フュージョン!!!”,然後這兩個數字就會融合,變成他們和的一半(向上取整)放到數組裡
現在有一個序列1,2,3,4,………,n,zwt想知道他要施法幾次,每一步選擇哪幾個數字,這個陣列才能變成一個數,並且使得這個最後留下的數字最小
如n=4,
1.選擇a=2,b=3,陣列變為[1,3,3]
2.選擇a=3,b=3,陣列變為[1,3]
3.選擇a=1,b=3,陣列變為[2]
如果有多種方案,輸出任意一種
Input
第一行輸入位t[1,1000],表示有t組資料
每組資料的第一行為n[2,200000]
n的總和不會超過200000
Output
對於每組資料輸出最後留下的最小的那個數字,之後n-1行依次輸出步驟
Example:
Input
1
4
Output
2
2 4
3 3
3 1
#include<stdio.h>
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,c=0;
int a[200001],b[200000][2];
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
a[i]=i;
}
for(int i=n;i>1;i--)
{
b[c][0]=a[i-1];
b[c][1]=a[i];
c++;
if((a[i]+a[i-1])%2==0)
a[i-1]=(a[i]+a[i-1])/2;
else
a[i-1]=(a[i]+a[i-1])/2+1;
if(i==2)
printf("%d\n",a[i-1]);
}
for(int i=0;i<c;i++)
{
printf("%d %d\n",b[i][0],b[i][1]);
}
}
return 0;
}
解題思路與收穫:
- 此題解法很多,形式比較自由。我使用的是官方推薦,即從後往前不斷求平均數(注意此題向上取整!)。還有一種即隨機選取.