程序設計思維與實踐 Week3 作業 (3/4/數據班)

A - 選數問題

題意

給定nn個數,從中選取kk個,另總和爲ss,共TT組數據。其中kn16k \le n \le 16T100T \le 100

input

1
10 3 10
1 2 3 4 5 6 7 8 9 10

output

4

思路

樸素的dfsdfs即可。雖然數據規模很小,但還是可以剪枝去掉一些情況。

  • 超過kk個數去掉
  • 總數超過ss去掉

最壞複雜度O(n!)O (n!)

總結

簽到題

代碼

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <vector>
#include <functional>
#define LL long long
using namespace std;
int v[20];
int n, k, s, ans;
void dfs(int x, int sum, int cnt) {
    // cout << "dfs: " << x << " " << sum << " " << cnt << endl;
    if (sum > s || cnt > k) return;
    if (cnt == k) {
        if (sum == s) {
            // cout << x << " " << sum << " " << k << endl;
            ans++;
        }
        return;
    }

    for (int i = x; i <= n; i++) {
        dfs(i + 1, sum + v[i], cnt + 1);
    }
}
int main() {
    int T;
    cin >> T;
    while (T) {
        T--;
        cin >> n >> k >> s;
        for (int i = 1; i <= n; i++) {
            cin >> v[i];
        }
        ans = 0;
        dfs(1, 0, 0);
        cout << ans << endl;
    }
    return 0;
}

B - 區間選點

題意

數軸上有 nn 個閉區間 [ai,bi][a_i, b_i]。取儘量少的點,使得每個區間內都至少有一個點(不同區間內含的點可以是同一個)。其中,(N<=100)(N<=100)(a,b<=100)(a,b<=100)

input

3
1 3
2 5
4 6

output

3
1 3
2 5
4 6

思路

以線段右端點bb爲序,從小到大排序。如果當前線段沒被覆蓋過。則將用頂點將該線段覆蓋,同時標記其他左端點aja_j在該線段bjb_j的線段爲覆蓋。

複雜度是O(n2)O(n^2),因爲每個線段只會被選擇一次。

正確性證明

由於以右端點bb爲序,對第ii條線段。若改線段未被覆蓋。那麼將點防止在bib_i處一定最優。因爲對於任意其它線段,線段的左端點aja_j一定要麼在bib_i的左邊,要麼在$ b_i$的右邊。

  • 若在$ b_j左邊,放在b_i$處顯然可以覆蓋更多線段
  • 若在bjb_j右邊,放在那裏都不會影響該線段。

假設當前有一個點沒有放在線段右頂點處,考慮將點重新放置於有頂點處不會使結果更差。

總結

一道入門貪心題,可用交換論證證明,即我的決策不會令答案更差

代碼

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#define LL long long
using namespace std;
struct re {
    int a, b;
    bool operator<(const re& a) const { return b < a.b; }
} v[110];
int vis[110];
int main() {
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> v[i].a >> v[i].b;
    }
    sort(v + 1, v + 1 + n);
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        if (!vis[i]) {
            ans++;
            for (int j = i + 1; j <= n; j++) {
                if (v[j].a <= v[i].b) {
                    vis[j] = 1;
                }
            }
        }
    }
    cout << ans;
    return 0;
}

C - 區間覆蓋

題意

數軸上有 nn ,(1n25000)(1\le n \le 25000)個閉區間$ [ai, bi]$,選擇儘量少的區間覆蓋一條指定線段 [1,t][1, t](1t1,000,000)( 1 \le t \le 1,000,000)
覆蓋整點,即(1,2)+(3,4)(1,2)+(3,4)可以覆蓋(1,4)(1,4)
不可能辦到輸出1-1

input

3 10
1 7
3 6
6 10

output

2

思路

講區間以左端點aa爲序,從小到大排序。對於一段未被覆蓋的區間[h,t][h,t]嗎,選取hh前的超過hh最多的線段,設選擇線段爲[ak,bk][a_k,b_k](假設bk<tb_k < t ,若bktb_k \ge t 則覆蓋完成得到答案)。仍未被覆蓋的區間變爲[bk+1,t][b_k+1,t],重複上述過程,直到bktb_k \ge t,若掃描完所有線段仍未覆蓋完所有區間,則輸出1-1

複雜度爲O(n)O( n )

小技巧

上述過程的複雜度可以達到爲O(n)O( n ),方法是排序後。標記一個flag爲仍未被覆蓋的線段的首屆點hh,順序掃描數組,對尾節點取maxmax,知道線段首屆點aa超過hh。(因爲我們並不關心選取的具體是哪一條線段)。

for (int i = 0; i < n; i++) {
    if (v[i].a > T || tail >= T) {
        break;
    }
    if (v[i].a <= flag) {
        tail = max(tail, v[i].b);
    } else {
        ans++;
        flag = tail + 1;  // 這裏注意
        if (v[i].a > flag) {
            tail = 0;
            break;
        } else {
            tail = max(tail, v[i].b);
        }
    }
}

總結

這道題吃了超多發WA,錯誤的願意是選擇區間[ak,bk][a_k,b_k]後,未覆蓋的區間應該變爲[bk+1,t][b_k+1,t],而不是[bk1,t][b_k-1,t]

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <cstring>
#include <string>
#include <algorithm>
#define LL long long
using namespace std;
struct re {
    int a, b;
    bool operator<(const re& a) const { return this->a < a.a; }
};
vector<re> v;
int main() {
    int n, T;
    while (~scanf("%d%d", &n, &T)) {
        v.clear();
        for (int i = 1; i <= n; i++) {
            re temp;
            scanf("%d%d", &temp.a, &temp.b);
            v.push_back(temp);
        }
        sort(v.begin(), v.end());
        int flag = 1, tail = 0, ans = 0;
        for (int i = 0; i < n; i++) {
            if (v[i].a > T || tail >= T) {
                break;
            }
            if (v[i].a <= flag) {
                tail = max(tail, v[i].b);
            } else {
                ans++;
                flag = tail + 1;  // 這裏注意
                if (v[i].a > flag) {
                    tail = 0;
                    break;
                } else {
                    tail = max(tail, v[i].b);
                }
            }
        }
        ans++;
        if (tail < T)
            printf("%d\n", -1);
        else
            printf("%d\n", ans);
    }
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章