第七屆山東省省賽題解
A - Julyed
類型:
水題
題意:
Julyed正在為她的大學英語六級考試做準備。她有N個字要記,但只剩M天了。如果她記不住這些話,她就不會通過大學英語六級考試。如果她不能通過大學英語六級考試,她就會不高興。但如果她在某一天記得太多的話,她也會不開心。如果她不高興,湯姆就會不高興。所以她會在一天之內記住盡可能少的單詞。為了快樂和快樂,在一天中,最多將會有多少個單詞會被記住呢?
題解:
水題 N / 天數 上取整
代碼:
#include<cstdio> #include<iostream> #include<cmath> using namespacestd; int main() { int T; cin >> T; while(T--) { int a,b; cin >> a >> b; int ans = ceil(a * 1.0/ b * 1.0); cout << ans << endl; } return 0; }
B - Fibonacci
題意:
給定一個整數N,判斷N是否可以寫成不連續的斐波那契數之和。若能輸不能輸出-1,若能輸出 N = f1 + f2 + f3 + … + fn.
題解:
首先不存在-1的情況。齊肯多夫定理:任何正整數都可以唯一地表示成若幹個不連續的斐波那契數之和。
證明:
數學歸納法,可以自行查找
那麽只需要打好斐波那契表,逆序遍歷Fib數,存到輸出數組裏,最後按照格式逆序輸出即可
int Fib[50]; int ans[50]; void init() { Fib[1]=1,Fib[2]=2; for(int i=3;i<=43;i++) Fib[i]=Fib[i-1]+Fib[i-2]; } int main() { init(); int T; cin>>T; while(T--) { int n; cin>>n; int len=0,put=n; for(int i=43;i>=1;i--) if(n>=Fib[i]) ans[++len]=Fib[i],n-=Fib[i]; cout<<put<<"="<<ans[len]; for(int i=len-1;i>=1;i--) cout<<"+"<<ans[i]; cout<<endl; } }
C - Proxy
題意:
尋找給定起點和目標點的最短路上,與起點相連的編號最小的節點序號。類型:圖論,思維
題解:
這是一道最短路問題。 要求有以下幾點:
①求最短路,如果沒有輸出-1;
②輸出最短路當中的距離起點最近的那個點
③存在多個最短路時,輸出最小的那個點
第一點套模版即可。
第二點,我們記錄路徑往往最容易查詢距離終點最近的那個點。所以我們可以建反向邊,這樣就可以查詢距離起點最近得了。
第三點,這是這道題的核心考點。 如果某點加上當前的路徑長度剛好等於最短路的長度,那麽說明存在兩條最短路了,這時候比較當前點和原本終點的前一個點的大小,選擇小的替換
題型:
圖論
代碼:
int INF = 1<<29; int mp[1010][1010]; int n,m; int d[1010]; int path[1010]; bool vis[1010]; void init(int n) { for(int i=0; i<n; i++) for(int j=0; j<n; j++) { if(i==j) mp[i][j]=0; else mp[i][j]=INF; } } void dijkstra() { int i,j,minn,v; for(i=0; i<n; i++) { vis[i]=0; d[i]=mp[n-1][i]; if(mp[n-1][i]<INF) path[i]=0; else path[i]=-1; } path[n-1]=0; for(i=0; i<n; i++) { minn=INF; for(j=0; j<n; j++) if(!vis[j] && d[j]<minn) { v=j; minn=d[j]; } vis[v]=1; for(j=0; j<n; j++) if(!vis[j] && d[j]>mp[v][j]+d[v]) { d[j]=mp[v][j]+d[v]; path[j]=v; } else { if(!vis[j]&&d[j]==mp[v][j]+d[v]) { path[j]=min(path[j],v); } } } } int main() { int t,a,b,c; cin>>t; while(t--) { cin>>n>>m; n+=2; init(n); for(int i=0; i<m; i++) { cin>>a>>b>>c; mp[b][a]=c; } dijkstra(); if(d[0]==INF) { cout<<"-1"<<endl; } else { cout<<path[0]<<endl; } } }
D - Swiss-system tournament
題意:
給出2*n個人,每次第1個人和第2個人比賽,第3個人和第4個人比賽…進行r輪比賽,每輪比完賽都進行排名,求出最後第q名是誰。給出每個人的積分 s[i],每個人的能力a[i],能力大的獲勝,獲勝的加1分,輸了的不加分。
類型:
思維 ,歸並思想
題解:
這個題目用到了歸並排序的思想,每輪比賽,把贏者放一組,輸者放一組,這樣單個數組也是有序的,然後進行合並。時間復雜度為0(2n*R)
代碼:
#include <stdio.h> #include <algorithm> using namespace std; struct node { int a,b; int index; }; node ans[200005]; node temp1[200005]; node temp2[200005]; bool cmp(node a,node b) { if(a.a==b.a) return a.index<b.index; return a.a>b.a; } int main() { int T; int n,r,q; int i,j,k,l; scanf("%d",&T); while(T--) { scanf("%d %d %d",&n,&r,&q); for(i=0; i<2*n; i++) { scanf("%d",&ans[i].a); ans[i].index=i+1; } for(i=0; i<2*n; i++) scanf("%d",&ans[i].b); sort(ans,ans+(2*n),cmp); for(i=0; i<r; i++) { int cnt1=0,cnt2=0; for(j=0; j<2*n; j+=2) { if(ans[j].b > ans[j+1].b) { ans[j].a++; temp1[cnt1++] = ans[j]; temp2[cnt2++] = ans[j+1]; } else { ans[j+1].a++; temp1[cnt1++] = ans[j+1]; temp2[cnt2++] = ans[j]; } } j=0; k=0; l=0; while(j<cnt1 && k<cnt2) { if(temp1[j].a == temp2[k].a) { if(temp1[j].index < temp2[k].index) { ans[l++] = temp1[j]; j++; } else { ans[l++] = temp2[k]; k++; } } else if(temp1[j].a > temp2[k].a) { ans[l++] = temp1[j]; j++; } else { ans[l++] = temp2[k]; k++; } } while(j<cnt1) ans[l++] = temp1[j++]; while(k<cnt2) ans[l++] = temp2[k++]; } printf("%d\n",ans[q-1].index); } return 0; }
E - The Binding of Isaac
題意:
就是找有多少個位置滿足只與‘#’有且僅有一條公共邊
題解:
暴力,數據量100 坑點 就是所給區域的外圍的區域也算,翻譯的時候會看到。
類型:
搜索,不過數據量比較水,是個簽到,不過背景很新。
代碼:
#include<stdio.h> int main() { char map[105][105]; int i,j,n,m,l,sum,k; scanf("%d",&l); while(l--) { k=0; sum=0; scanf("%d%d",&m,&n); getchar(); for(i=0;i<105;i++) for(j=0;j<105;j++) map[i][j]=0; for(i=1;i<=m;i++) { for(j=1;j<=n;j++) scanf("%c",&map[i][j]); getchar(); } for(i=1;i<=m;i++) for(j=1;j<=n;j++) { if(map[i][j]==‘#‘) { if(map[i+1][j]==0) sum++; if(map[i-1][j]==0) sum++; if(map[i][j+1]==0) sum++; if(map[i][j-1]==0) sum++; } if(map[i][j]==‘.‘) { if(map[i+1][j]==‘#‘) k++; if(map[i-1][j]==‘#‘) k++; if(map[i][j+1]==‘#‘) k++; if(map[i][j-1]==‘#‘) k++; if(k==1) sum++; k=0; } } printf("%d\n",sum); } return 0; }
F - Feed the monkey
真~不會
G - Triple Nim
題意:
有一堆石子,一共有n個,把n個石子分成三堆,求有多少種分配的方式能夠使得bob win?
題解:
算是兩個題解吧。
第一:非正式的,就是打表找規律。
規律如下:
如果 n 是 奇數,直接輸出 0.
如果是偶數,並且是 2 的某個次方,輸出 0.
否則統計二進制中 1 的個數。
兩個 1 答案為 1
三個 1 答案為 4
四個 1 答案為 13
五個 1 答案為 40
六個 1 答案為 121
可以把答案單獨開個數組 F[ N ] = F [ N -1 ] * 3 +1;
第二:正式的,這就是個Nim博弈
尼姆博弈是利用二進制的思想,那麽本題也可以利用二進制的思想,可知,如果要使得Alice輸並且Alice為先手,只需要使得三堆石子異或等於0 即可,首先共有n個石子,把n轉化成二進制來表示,假設其中有k個1存在,如果要使得三堆石子異或為0,則在三堆石子數的二進制數的每位上1的個數都要是偶數位,又可知,在二進制中,高位的1是由低位進位而得到的,也就是說高位的1可以分解成兩個低位的1,當n是奇數的時候,最低位為1且沒有辦法分解,所以輸出0,所以當n為偶數的時候,就有(3^k - 3)/6個,減去3是去掉一個為0的情況,除6是應為本題求得是排列。
題型:
博弈
代碼1:
#include<iostream> #include<cstdio> #include<cstring> using namespace std; long long int f[50]; int main() { int T; cin>>T; f[1]=0; f[2]=1; for(int i=3;i<=49;i++) f[i]=f[i-1]*3+1; while(T--) { long long int n; cin>>n; if(n%2) { cout<<0<<endl; } else { long long int x=n; int num=0; while(x) { if(x%2) num++; x/=2; } cout<<f[num]<<endl; } } }
代碼2:
#include <bits/stdc++.h> using namespace std; int main() { int t; scanf("%d",&t); while(t--) { long long n; cin>>n; if(n%2)cout<<"0"<<endl; else { int num=0; while(n) { if(n%2)num++; n=n/2; } long long ans=(pow(3,num)-3)/6; cout<<ans<<endl; } } return 0; }
H - Memory Leak
題意:
內存泄漏是C/ c++中一個眾所周知的bug。當一個字符串比預期的長時,它將訪問下一個數組的內存,這將導致問題並泄漏一些信息。你可以看到一個簡單的例子:
如我們所見,如果輸入字符串的長度等於或大於數組的極限長度,則額外的部分將不會被存儲,下一個數組的信息將在輸出時被泄漏。輸出將停止,直到找到“\0”字符(字符串末尾的符號)。在這個問題中,程序永遠不會有意外的結束,最後一個數組不會泄漏其他信息。
提供的源代碼如下:
INPUT
多個測試用例,第一行是整數T (T <= 20),表示測試用例的數量。
每個測試用例的第一行包含一個非空字符串,即字符串的定義,格式化為“char s1[s1_len], s2[s2_len]…”。“char”是數組的類型,它永遠不會改變。s1,s2……是數組的名稱。s1_len s2_len…長度限制。如果沒有出錯,數組應該能夠存儲輸入字符串和“\0”。不同數組的定義將用逗號和空格分開。長度限制為正,長度限制之和小於10000。
然後,將會有幾行字符串,其中包含兩到三個部分。
第一部分是“get”或“cout”,第二部分是字符串s,表示數組的名稱。s將只包含小寫字母和數字數字,並以字母開頭。在一個例子中s是不同的。如果第一部分是“get”,那麽將會有第三部分,一個字符串,該字符串應該被輸入到數組s中,輸入字符串的長度將小於1000,並且只包含可見的ASCII字符。“get”操作將重寫數組,無論之前數組中是什麽,然後在字符串後面添加“\0”。不同的部分被一個空間隔開。
Case以“return 0”結尾;
整個輸入文件小於10MB。
對於每個“cout”,您應該輸出一行包含實際輸出的內容,這意味著您應該考慮內存泄漏的問題。如果請求的數組為空,則應該輸出空行。
類型:
大模擬題 ,字符串處理。還有對英語也是一種考驗,很容易漏條件。
題解:
判每個數組的類型和長度,然後就檢查有沒有內存泄漏。
代碼:
#include <bits/stdc++.h> using namespace std; struct node { int l,r; }e[11111]; int top; char s[11111]; char c[11111][1111]; char op[11111]; int main() { int T; scanf("%d",&T); while(T--) { memset(s,0,sizeof(s)); int pos = 0; top = 0; scanf("%s",op); while(1) { scanf("%s",op); int len = strlen(op); int num = 0,i = 0; for(i = 0;i < len; i++) { if(op[i] != ‘[‘) c[top][i] = op[i]; else break; } c[top][i] = ‘\0‘; for(i++ ;i < len; i++) { if(op[i] == ‘]‘) break; num = num * 10 + op[i] -‘0‘; } e[top].l = pos; e[top].r = pos+num; pos+=num; top++; char ss = getchar(); if(ss == ‘\n‘) break; } while(1) { scanf("%s",op); if(op[0] == ‘r‘) { scanf("%s",op); break; } if(op[0] == ‘c‘) { scanf("%s", op); for(int i = 0;i < top; i++) { if(strcmp(op,c[i]) == 0) { for(int j = e[i].l ; j < pos; j++) { if(s[j] == ‘\0‘) break; printf("%c",s[j]); } printf("\n"); break; } } } else if(op[0] == ‘g‘) { scanf("%s",op); for(int i = 0 ;i < top; i++) { if(strcmp(op,c[i]) == 0) { gets(op); int len = strlen(op); int k = 1,j; for( j = e[i].l ; j < e[i].r && k < len; j++, k++) { s[j] = op[k]; } if(j < e[i].r) { s[j] = ‘\0‘; } break; } } } } } return 0; }
I - Rock Paper Scissors
真~不會
J - Execution of Paladin
題意:
先來說一下每個卡牌是什麽意思,左上角寫著10的那個牌叫“亡者歸來”,作用是召喚7個死亡的魚人,
下面的四個魚人從左到右分別是:
寒光智者(攻2):作用是每個玩家抽兩張牌。對這個題來說沒什麽用。
魚人領軍(攻3);所有的其他魚人的攻擊力+2
藍鰓戰士(攻2);沖鋒(就是一上來就能打,其他的需要休息一回合。)
老瞎眼(攻2):沖鋒,並且場上每有一個其他魚人,他的攻擊力就+1.
題解:
算斬殺,就是看看這一回合能造成多少傷害,
先算一共的魚人個數,再算領軍的數量,在分別算藍鰓和老瞎眼的個數,因為這兩種可以上來就打,
傷害量 = 2 * 領軍 + 藍鰓 * 2 + 2 *領軍 + (魚人個數+2)* 老瞎眼。
類型:
模擬
代碼:
#include<cstdio> #include<iostream> using namespace std; int main() { int T; cin >> T; char a[50]; int o,b,c,m; while(T--) { int n,e; o = 0,b = 0,c = 0,m = 0; cin >> n >> e; getchar(); for(int i = 1; i <= n; ++i) { gets(a); if(a[0] == ‘O‘) o++; if(a[0] == ‘C‘) c++; if(a[0] == ‘M‘) m++; if(a[0] == ‘B‘) b++; } int sha = o * (2 + 2 * m + n - 1) + b * (2 + 2 * m); if(sha >= e) { cout << "Mrghllghghllghg!" << endl; } else { cout << "Tell you a joke, the execution of Paladin." << endl; } } return 0; }
K - Reversed Words
題意:
字符串反轉
題解:
就是將每個單詞進行轉置。
類型:
字符串操作
#include<cstdio> #include<iostream> #include<cstring> using namespace std; const int M = 1e6; char a[M]; char b[M]; int main() { int T; cin >> T; getchar(); while(T--) { char c; memset(a,0,sizeof(a)); memset(b,0,sizeof(b)); for(int i = 0;;++i) { scanf("%c",&a[i]); if(a[i] == 32) { for(int j = i - 1; j >= 0;--j) { printf("%c",a[j]); } cout << ‘ ‘; i = -1; } if(a[i] == ‘\n‘) { for(int j = i - 1; j >= 0; --j) { printf("%c",a[j]); } cout << endl; break; } } } }
L - Password
真~不會
第七屆山東省省賽題解