## Promise ### 解題思路 這是一題非常引人深思的題目,綜合了助教們當助教以來的所有心血所出的題目。它看似平平無奇卻又富含深度,充分考驗著學生對於程式設計課的理解。只要你多花一點時間看看這題你就會發現,你花了一點時間。 好的,~~以上幹話結束。~~ 注意測資範圍,小心被卡long long。 ### AC code ```c= #include<stdio.h> #include<stdlib.h> int main(){ long long n; scanf("%lld",&n); printf("%lld\n",n*50); return 0; } ``` ## Sneaker ### 解題思路 這題其實就只是費式數列的小改版,只需要保證時間複雜度不是$O(2^n)$,就不會被卡掉。最簡單的解法是用loop去解,想要加速也可以用矩陣快速冪去解。 ### AC code #### Loop $O(n)$解 ```c= #include<stdio.h> #include<stdlib.h> int main(){ int t; int a,b,c,d,n; scanf("%d",&t); while(t--){ scanf("%d%d%d%d%d",&a,&b,&c,&d,&n); long long *nums = (long long *)calloc(n,sizeof(long long)); nums[0] = a; nums[1] = b; for(int i = 2 ; i < n ; i++){ nums[i] = c*nums[i-1]+d*nums[i-2]; nums[i]%=1000000007; } printf("%lld\n",nums[n-1]); } return 0; } ``` #### 矩陣快速冪 $O(log n)$解 ```c= #include<stdio.h> #include<stdlib.h> #include <time.h> #define MAX 1000000007 long long int** matrix_mult(long long int **A,long long int **B){ long long int **sum = (long long int**)malloc(2*sizeof(long long int*)); for(int i = 0 ; i < 2 ;i++){ sum[i] = (long long int*)calloc(2,sizeof(long long int)); } for(int i = 0 ; i < 2 ; i++){ for(int j = 0 ; j < 2 ; j++){ for(int k = 0 ; k < 2 ; k++){ sum[i][j] += (A[i][k]*B[k][j])%MAX; sum[i][j]%=MAX; } } } return sum; } long long int** fast_compute(long long int **A,int n){//n是次方 if(n == 1) return A; if(n%2 == 0){ return fast_compute(matrix_mult(A,A),n/2); } else{ return matrix_mult(A,fast_compute(matrix_mult(A,A),(n-1)/2)); } } int main(){ long long int t; scanf("%lld",&t); long long int **start_matrix = (long long int**)malloc(2*sizeof(long long int*)); for(int i = 0 ; i < 2 ;i++){ start_matrix[i] = (long long int*)calloc(2,sizeof(long long int)); } long long int **mult_matrix = (long long int**)malloc(2*sizeof(long long int*)); for(int i = 0 ; i < 2 ; i++){ mult_matrix[i] = (long long int*)calloc(2,sizeof(long long int)); } while(t--){ long long int a,b,c,d,n; scanf("%lld %lld %lld %lld %lld",&a,&b,&c,&d,&n); start_matrix[0][0] = a*d+b*c;//f(3) start_matrix[0][1] = b; start_matrix[1][0] = b; start_matrix[1][1] = a; mult_matrix[0][0] = c; mult_matrix[0][1] = d; mult_matrix[1][0] = 1; mult_matrix[1][1] = 0; if(n == 1) printf("%lld\n",a); else if(n == 2) printf("%lld\n",b); else if(n == 3) printf("%lld\n",start_matrix[0][0]); else{ long long int **B = fast_compute(mult_matrix,n-3); long long int **ans = matrix_mult(B,start_matrix); printf("%lld\n",ans[0][0]%MAX); } } return 0; } ``` ## XYZ ### 相關演算法 * Stack ### 解題思路 這題使用到了 Stack 的概念,配和指標的移動,對輸入的算試做處理。先從字串的第一個字元開始(*expr),如果是 '(' ,就把之後的兩個數字利用移動指標的方式,型成一個新指標並放入堆疊,最後在將指標移動到 ')' 之後的運算符號上,當下次迴圈執行時,如果指到的是 '*' 則將堆疊最上面的向量和下一個向量做內積,由於是拿出一個向量再放入一個向量,所以 vtop 不變,如果是 '+' 則直接跳過,最後指標指到字串結尾時 (*expr == '\0') 迴圈結束,最後將所有在堆疊中的向量相加,便可以達到先內積後加減的效果。 ### AC code ```c= #include <stdio.h> #include <stdlib.h> // 定義向量結構 typedef struct Vec { int x; int y; } Vec; int main() { char *expr = (char *)calloc(10000, sizeof(char)); scanf("%s", expr); // 定義堆疊 Vec *vstack = (Vec *)calloc(2000, sizeof(Vec)); int vtop = 0; while (*expr != '\0') { switch (*expr) { // 將下個向量加入堆疊並將指標向後移動 case '(': expr++; Vec v; v.x = *expr - '0'; expr += 2; v.y = *expr - '0'; expr++; vstack[vtop++] = v; break; // 將堆疊中最上面的向量和新的向量做內積 case '*': expr += 2; vstack[vtop - 1].x *= *expr - '0'; expr += 2; vstack[vtop - 1].y *= *expr - '0'; expr++; break; // 加號,先跳過 default: expr++; break; } } // 將堆疊中的所有向量相加 while (vtop > 1) { vstack[vtop - 2].x += vstack[vtop - 1].x; vstack[vtop - 2].y += vstack[vtop - 1].y; vtop--; } printf("%d %d\n", vstack[0].x, vstack[0].y); // 釋放記憶體 free(vstack); return 0; } ``` ## Final_Battle ### 相關演算法 * sorting * greedy ### 解題思路 這題的關鍵是找到 "第一個違反最佳字母擺放順序" 的位置 ex: eacbd 中,第二個位置的最佳字母應為 "d",但 "d" 卻跑到第五個位置去了 於是我們想辦法把 "d" 拉到正確的位置去就好了 (透過反轉),剩餘的實作細節請參考程式碼 ``` 給定字串: eacbd 最佳答案: edcba ``` ### AC code ```c= #include <stdio.h> typedef long long int llt; int cmp(const void* a, const void* b) { return *(char*)a<*(char*)b; } llt Size(char s[30]) { llt i=0; while(1) { if(s[i]=='\0') return i; i++; } } int main() { llt t; scanf("%lld\n", &t); for(llt i=0;i<t;i++) { char s[30]; scanf("%s", s); llt size_s=Size(s); char s_sorted[30]; for(llt j=0;j<size_s;j++) { s_sorted[j]=s[j]; } llt a[26]; // 存字母在原字串中出現的位置 (0-indexed) for(llt j=0;j<26;j++) { a[j]=-1; } llt j=0; while(s[j]!='\0') { a[s[j]-'a']=j; j++; } qsort((void*)s_sorted, size_s, sizeof(s_sorted[0]), cmp); llt left=0, right=-1; while(1) { if(left==size_s) break; if(s[left]==s_sorted[left]) { left++; continue; } else { //printf("%lld %lld\n", left, a[s_sorted[left]-'a']); right=a[s_sorted[left]-'a']; break; } } if(right!=-1) { char s_temp[30]; for(llt j=0;j<size_s;j++) { s_temp[j]=s[j]; } llt r=right; for(llt j=left;j<=right;j++) { s[j]=s_temp[r]; r--; } } for(llt j=0;j<size_s;j++) { printf("%c", s[j]); } printf("\n"); } } ``` ## Death_Zone ### 相關演算法 * 二分搜 ### 解題思路 這題是一題藏的很深的 binary search 的題目,根據題目給定的奇怪公式 i\*j + max(i, j)\*max(i, j),可以觀察到當 i 變大、j 不變的情況下,計算出來的命定值保證變大 如此一來便可以針對每個 column 做一次二分搜,快速找出有幾個元素小於芙莉連所選座標的命定值了 時間複雜度: O(nlogn) ![image](https://hackmd.io/_uploads/S1UIyBvZ0.png =60%x) ### AC code ```c= # include <stdio.h> typedef long long int llt; llt CalculateMax(llt a, llt b) { if(a>b) return a; else return b; } int main() { llt n, x, y; scanf("%lld %lld %lld", &n, &x, &y); llt target=x*y+CalculateMax(x, y)*CalculateMax(x, y); llt ans=0; for(llt j=1;j<=n;j++) { llt left=0, right=n, mid; llt Max=0; while(1) { mid=(left+right)/2; if(mid*j+CalculateMax(mid, j)*CalculateMax(mid, j)<=target) { Max=CalculateMax(Max, mid); } if(left==right) break; if(mid*j+CalculateMax(mid, j)*CalculateMax(mid, j)<=target) left=mid+1; else right=mid; } ans+=Max; } printf("%lld\n", ans); return 0; } ``` ## Chest_Hunter ### 相關演算法 * BFS ### 解題思路 這題使用到了 BFS 的技巧 (counter 初始化成 0) 1. 先從起點把所有可以走到的位置走完 2. 炸掉跟 "目前可以走到的位置" 相鄰的所有牆壁,且counter ++ 3. 反覆執行第 1、2 步,直到走到其中一個寶箱怪所在位置,此時 counter 即為答案 這邊需要注意的是,第二步炸掉的是一整層牆壁,而非一格牆壁,這是因為炸掉牆壁的過程是同步進行的 (就像 BFS 找最短路徑時,也是同時像各方向擴散一格一樣) 時間複雜度: O(n*m) ``` ans=0 .#####V# .####### .####.#V .###..## .###..#. ans=0 O#####V# O####### O####.#V O###..## O###..#. ans=1 O.####V# O.###### O.###.#V O.##..## O.##..#. ans=1 OO####V# OO###### OO###.#V OO##..## OO##..#. ans=2 OO.###V# OO.##### OO.##.#V OO.#..## OO.#..#. ans=2 OOO###V# OOO##### OOO##.#V OOO#..## OOO#..#. ans=3 OOO.##V# OOO.#### OOO.#.#V OOO...## OOO...#. ans=3 OOOO##V# OOOO#### OOOO#O#V OOOOOO## OOOOOO#. ans=4 OOOO.#V# OOOO..## OOOO.O.V OOOOOO.# OOOOOO.. ``` ### AC code ```c= # include <stdio.h> // 定義座標結構 typedef struct { int x; int y; } Coordinate; // 定義循環隊列 #define MAX_SIZE 100001 typedef struct { Coordinate data[MAX_SIZE]; int front, rear; int size; // 新增隊列大小的變數 } CircularQueue; // 初始化循環隊列 void initQueue(CircularQueue *queue) { queue->front = -1; queue->rear = -1; queue->size = 0; // 初始化隊列大小為 0 } // 檢查循環隊列是否為空 int isEmpty(CircularQueue *queue) { return (queue->front == -1 && queue->rear == -1); } // 檢查循環隊列是否已滿 int isFull(CircularQueue *queue) { return ((queue->rear + 1) % MAX_SIZE == queue->front); } // 將元素推入循環隊列 void push(CircularQueue *queue, Coordinate coord) { if (isFull(queue)) { printf("Queue is full. Cannot push element.\n"); return; } else if (isEmpty(queue)) { queue->front = 0; queue->rear = 0; } else { queue->rear = (queue->rear + 1) % MAX_SIZE; } queue->data[queue->rear] = coord; queue->size++; // 推入元素後增加隊列大小 } // 將元素彈出循環隊列 Coordinate pop(CircularQueue *queue) { Coordinate poppedCoord; if (isEmpty(queue)) { printf("Queue is empty. Cannot pop element.\n"); poppedCoord.x = -1; // 表示出錯 poppedCoord.y = -1; return poppedCoord; } else if (queue->front == queue->rear) { poppedCoord = queue->data[queue->front]; queue->front = -1; queue->rear = -1; } else { poppedCoord = queue->data[queue->front]; queue->front = (queue->front + 1) % MAX_SIZE; } queue->size--; // 彈出元素後減少隊列大小 return poppedCoord; } // 查看循環隊列的頂部元素 Coordinate top(CircularQueue *queue) { Coordinate topCoord; if (isEmpty(queue)) { printf("Queue is empty. Cannot get top element.\n"); topCoord.x = -1; // 表示出錯 topCoord.y = -1; return topCoord; } else { return queue->data[queue->front]; } } // 取得隊列大小 int getSize(CircularQueue *queue) { return queue->size; } int main() { int n, m; scanf("%d %d\n", &n, &m); char Map[n][m]; int visited[n][m]; int dx[4]={1, -1, 0, 0}; int dy[4]={0, 0, 1, -1}; for(int i=0;i<n;i++) { for(int j=0;j<m;j++) { scanf("%c", &Map[i][j]); visited[i][j]=0; } if(i!=n-1) scanf("\n"); } CircularQueue queue; initQueue(&queue); Coordinate start={0, 0}; push(&queue, start); visited[0][0]=1; int counter=0; while(!isEmpty(&queue)) { CircularQueue temp_queue; // 存那些下一輪會被炸掉的牆壁位置 initQueue(&temp_queue); while(!isEmpty(&queue)) { Coordinate poppedCoord = pop(&queue); if(Map[poppedCoord.x][poppedCoord.y]=='V') { printf("%d\n", counter); return 0; } for(int i=0;i<4;i++) { int x=poppedCoord.x+dx[i]; int y=poppedCoord.y+dy[i]; if(x<n && x>=0 && y<m && y>=0 && visited[x][y]==0) { if(Map[x][y]!='#') { Coordinate coord = {x, y}; push(&queue, coord); visited[x][y]=1; } else { Coordinate coord = {x, y}; push(&temp_queue, coord); } } } } while(!isEmpty(&temp_queue)) { Coordinate poppedCoord = pop(&temp_queue); push(&queue, poppedCoord); visited[poppedCoord.x][poppedCoord.y]=1; } counter++; } printf("-1\n"); return 0; } ``` ## Puzzle ### 相關演算法 * DFS ### 解題思路 這一題是類似解數獨的題目,用DFS的方式,每一次對於未解出的格子選一個可能的解,再往下一格試試 如果錯誤了就會退回上一格繼續嘗試下一個可能 ### AC code ```c= #include <stdio.h> #include <stdbool.h> #include <stdlib.h> bool check(char **map, int x, int y, char symbol){ for(int i = 0; i < 9; i++){ if(map[x][i] == symbol || map[i][y] == symbol){ return false; } } int start_x = x - x % 3; int start_y = y - y % 3; for(int i = start_x; i < start_x + 3; i++){ for(int j = start_y; j < start_y + 3; j++){ if(map[i][j] == symbol){ return false; } } } return true; } bool solve(char *label, char **map, int x, int y){ if(y == 9){ y = 0; x++; if(x == 9){ return true; } } if(map[x][y] != '.'){ return solve(label, map, x, y+1); } for(int i = 0; i < 9; i++){ if(check(map, x, y, label[i])){ map[x][y] = label[i]; if(solve(label, map, x, y+1)){ return true; } map[x][y] = '.'; } } return false; } int main(){ char *label = malloc(10 * sizeof(char)); char **map = malloc(9 * sizeof(char *)); for(int i = 0; i < 9; i++) map[i] = malloc(10 * sizeof(char)); scanf("%s", label); for(int i = 0; i < 9; i++){ scanf("%s", map[i]); } solve(label, map, 0, 0); for(int i = 0; i < 9; i++){ printf("%s\n", map[i]); } return 0; } ``` ## Holiday ### 相關演算法 * 暴力搜尋法 ### 解題思路 簡單版: 這題由於 n 非常小,只要能暴力找出每一組解,便可以通過這題 以 n = 3 舉例,六個數字依序代表: 第一個人收到的第一份祝福來自? 第一個人收到的第二份祝福來自? 第二個人收到的第一份祝福來自? 第二個人收到的第二份祝福來自? 第三個人收到的第一份祝福來自? 第三個人收到的第二份祝福來自? ![image](https://hackmd.io/_uploads/BkSLtC7ZA.png) 可以觀察出一些規則 1. 答案是 1~n 各出現兩次的 permutation (祝福隨便分配給所有人) 2. 但同一個人分到的兩份祝福,要按照遞增順序排序 (例如 12 12 33、13 12 23 等等),這是因為"某個人先收到第 1 個人的祝福再收到第 2 個人的祝福"、以及"某個人先收到第 2 個人的祝福再收到第 1 個人的祝福",這兩種情況是完全相同的,故只需要算一次就好 ### AC code ```c= #include <stdio.h> typedef long long int llt; llt counter=0; llt n; llt a[8*2]; llt used[8*2]; void f(llt now_index) { if(now_index>=2*n) { for(llt i=0;i<2*n;i+=2) { if(a[i+1]<a[i]) return; } counter++; for(llt i=0;i<2*n;i++) { printf("%d ", a[i]); } printf("\n"); return; } for(int i=0;i<n;i++) { if(used[i]<2) { used[i]++; a[now_index]=i+1; f(now_index+1); used[i]--; } } } void Solve() { scanf("%lld", &n); //pirntf("第一個人收到的第一份祝福來自? 第一個人收到的第二份祝福來自? 第二個人收到的第一份祝福來自? 第二個人收到的第二份祝福來自? 第三個人收到的第一份祝福來自? 第三個人收到的第二份祝福來自?"); counter=0; for(llt i=0;i<n*2;i++) { used[i]=0; } f(0); printf("%lld\n", counter); } int main() { llt t=1; for(llt i=1;i<=t;i++) { Solve(); } return 0; } ``` ## Holiday_extra ### 相關演算法 * 動態規劃 ### 解題思路 我們可以將每個人的情況分成 3種 A. 還沒被分配到 B. 被分配到一個 C. 都被分配完了 每次分配兩個箭頭,就會有四種不同的分法 1. 兩個箭頭都給同一個人 (當然這種人肯定是A情況,給完後變成C) 2. 兩個箭頭給不同人(給兩個A,給完後這兩個變成B) 3. 兩個箭頭給不同人(給一個A 一個B,給完後A變成B,B變成C) 4. 兩個箭頭給不同人(給兩個B,這兩個人都變成C) 可以先把答案定義成F(A的數量,B的數量) F(A,B)= 第一種分法 * F(A-1,B) \+ 第二種分法 * F(A-2, B+2) \+ 第三種分法 * F(A-1, B) \+ 第四種分法 * F(A, B-2) 因此,若是題目輸入n,就代表一開始有n個A情況的人需要被分配 也就是F(n, 0) 只要把上述的算式寫出來,記得加一些判斷式 不要讓A 或 B 變成負數 譬如 F(A,B-2) 就肯定B要>=2 同時也要記得加上算完的中止情況,否則會無止盡的算完下去 也就是 F(1,0)=F(0,2)=1 F(1,0)代表剩一個人都還沒分到,那兩個箭頭都給他,只有這種情況 F(0,2)代表剩兩個人都差一個箭頭,因此把箭頭都各自分給他們,也只有這種情況 ### AC code ```c= #include <stdio.h> #include <stdlib.h> #define int long long int mod = 1e9 + 7; int **arr = NULL; int f(int d, int s){ if(d < 0 || s < 0){ return 0; } if(arr[d][s] != 0){ return arr[d][s]; } if(d >= 1 && arr[d-1][s] == 0){ arr[d-1][s] = f(d-1, s); } if(s >= 2 && arr[d][s-2] == 0){ arr[d][s-2] = f(d, s-2); } if(d >= 2 && arr[d-2][s+2] == 0){ arr[d-2][s+2] = f(d-2, s+2); } int ans = 0; if(d >= 1) ans += (d * (s+1) * arr[d-1][s] % mod); if(s >= 2) ans += ((s * (s-1) / 2) * arr[d][s-2] % mod); if(d >= 2) ans += ((d * (d-1) / 2) * arr[d-2][s+2] % mod); arr[d][s] = ans % mod; return ans % mod; } int main(){ int n; scanf("%lld", &n); arr = (int **)malloc((n+1) * sizeof(int*)); for(int i = 0; i <= n; i++){ arr[i] = (int *)calloc(2*n+1, sizeof(int)); } arr[1][0] = 1; arr[0][2] = 1; f(n, 0); printf("%lld\n", arr[n][0]); } ```