---
# System prepended metadata

title: S3 演算法 week 4
tags: [S3 公開資源]

---

# S3 演算法 week 4
###### tags: `S3 公開資源`

:::info
時間：11/13 9:00 ~ 17:00
地點：成大資工系新館 **65304** 教室
主題：複雜度分析與程式技巧
直播連結：https://youtu.be/ecpPlY5Xk-M
:::

## 課程內容
因進度調整，複雜度與程式技巧延至本周上課
- 複雜度分析
- 程式技巧
- 講義連結：https://hackmd.io/@sa072686/cp/%2F%40sa072686%2FrJpY0i7PS


## 注意事項
1. 本次上課會開始進到演算法的領域，請務必熟悉前三週的內容，並預習講義第二章之後的內容
2. 上次練習賽的題解已經上傳至 YouTube：https://youtu.be/8U8OAURzDew
3. 所有上課相關連結都會放在資源彙整裡：https://hackmd.io/@SCIST/rykldedMj
4. 上課期間請全程配戴口罩
5. 建議學員可以帶自己的筆電，以減少重新設定的時間成本
6. 請假表單：https://forms.gle/2ESVuTezcHCK959H6

# 必作題題解

[TOC]

## 時間複雜度分析_解答

### P1
 $O(n^3)$
 補充：這是最短路徑演算法中的 Floyd–Warshall 可以知道每個點到每個點的最短距離

### P2
 $O(n\log n)$
 
### P3
 $O(nk)$
 補充：選擇排序

### P4
 $O(n^3)$


## 初章後半

### [ZJ c290](https://zerojudge.tw/ShowProblem?problemid=c290)

撰寫者：小麥

用string一個一個的讀出來，再char轉int，之後相減。

:::spoiler 參考程式碼
```cpp=
#include <iostream>
#include <string>

using namespace std;

int main() {

    string input;
    cin >> input;
    
    int len = input.length();

    int odd_sum = 0;
    for (int i = 1; i < len; i+=2) {
        odd_sum += input[i] - '0';
    }
    int even_sum = 0;
    for (int i = 0; i < len; i+=2) {
        even_sum += input[i] - '0';
    }

    int ans = odd_sum - even_sum;
    if (ans < 0) {
        cout << (ans * -1) << '\n';
    }
    else {
        cout << ans << '\n';
    }

    return 0;

}
```
:::

---

### [ZJ h082](https://zerojudge.tw/ShowProblem?problemid=h082)

撰寫者：

---
### [ZJ f606](https://zerojudge.tw/ShowProblem?problemid=f606)

撰寫者：fishhh

這裡就提供參考程式碼
若需要更詳細的解題報告，請參考講師的講義~

:::spoiler
```cpp=
#include "iostream"
using namespace std;
#define int long long
signed main(){
    int n,m,k;
    int q[60][60]={};
    cin>>n>>m>>k;
    for(int y=0;y<n;y++){
        for(int x=0;x<m;x++){
            cin>>q[x][y];
        }
    }
    int minn=1e9;
    while(k--){
        int ans=0;
        int sum[60][60]={};
        int l;
        for(int i=0;i<n;i++){
            cin>>l;
            for(int v=0;v<m;v++){
                sum[l][v]+=q[v][i];
            }
        }

        for(int y=0;y<m;y++){
            for(int x=0;x<m;x++){
                if(x==y){
                    ans+=sum[x][y];
                }
                else if(sum[x][y]<=1000){
                    ans+=sum[x][y]*3;
                }
                else{
                    ans+=3000;
                    ans+=(sum[x][y]-1000)*2;
                }
            }
        }
        minn= min(minn,ans);
    }
    cout<<minn;
}
```
:::

---
## 二章 - 第一節

### [UVa 483](http://domen111.github.io/UVa-Easy-Viewer/?483)

撰寫者：Jun-an

使用 getline(cin, str) 讀入整行字串，接著遍歷字串，
宣告一個字串變數 temp 儲存遍歷過的字元，
如果讀到空白就將 temp 反著輸出，再把 temp 清空，
因為是讀到空白再輸出，如果沒有空白就不會有輸出，
所以輸入字串後在後面加上一個空白。

:::spoiler 參考程式碼
```cpp=
#include <iostream>

using namespace std;

int main() {
    string sentence;

    while (getline(cin, sentence)) {
        sentence += " ";

        string temp;
        bool appeared = false;

        for (int i = 0; i < sentence.size(); i++) {
            if (sentence[i] == ' ') {
                if (appeared) {
                    cout << " ";
                }

                for (int j = temp.size(); j > 0; j--) {
                    cout << temp[j - 1];
                }

                temp = "";
                appeared = true;

                continue;
            }

            temp += sentence[i];
        }

        cout << '\n';
    }

    return 0;
}
```
:::

---

## 二章 - 第二節

### [ZJ b840](https://zerojudge.tw/ShowProblem?problemid=b840)

撰寫者：小麥

窮舉每個點當作左上角的起點，然後用兩層for迴圈窮舉所有子矩形的大小(長&寬) 再遍歷

所以最後程式長相會是有6個for迴圈包在一起

時間複雜度：$O(n^6)$ 
$n \leq 20$ 所以 $n^6$ 的計算量可以在 1 秒內跑完!

補充：

先用建立二維前綴和，在枚舉起點和終點。
時間複雜度：$O(n^2)$

:::spoiler 參考程式碼
```cpp=
#include <iostream>

using namespace std;

int nums[20][20];
int prefixSum[20 + 1][20 + 1];

int main() {

    int n;
    
    cin >> n;
    
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            cin >> nums[i][j];
        }
    }

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            prefixSum[i][j] = (prefixSum[i-1][j] - prefixSum[i-1][j-1]) + prefixSum[i][j-1] + nums[i-1][j-1];
        }
    }
    
    int maxi = 0;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            for (int o = i; o <= n; o++) {
                for (int k = j; k <= n; k++) {
                    int sum = prefixSum[o][k] - prefixSum[o][j-1] - prefixSum[i-1][k] + prefixSum[i-1][j-1];
                    if (sum > maxi) {
                        maxi = sum;
                    }
                }
            }
        }
    }

    cout << maxi << '\n';

    return 0;

}
```
:::

---

### [ZJ f312](https://zerojudge.tw/ShowProblem?problemid=f312)

撰寫者：小麥

先從第一個工廠有最多的員工開始，第一個工廠-1，第二個工廠就+1，一直到第一個工廠到0為止

:::spoiler 參考程式碼
```cpp=
#include <iostream>

using namespace std;

int main() {

    int A1, B1, C1;
    int A2, B2, C2;
    int n;

    cin >> A1 >> B1 >> C1;
    cin >> A2 >> B2 >> C2;
    cin >> n;

    int maxi = -1000000;
    int left_pointer = n;
    int right_pointer = 0;

    while (right_pointer != n + 1) {
        int Y1 = A1 * (left_pointer * left_pointer) + B1 * left_pointer + C1;
        int Y2 = A2 * (right_pointer * right_pointer) + B2 * right_pointer + C2;
        int sum = Y1 + Y2;
        if (maxi < sum) {
            maxi = sum;
        }
        left_pointer--;
        right_pointer++;
    }

    cout << maxi << '\n';

    return 0;

}
```
:::

---


