A - 選數問題
題意
給定個數,從中選取個,另總和爲,共組數據。其中,。
input
1
10 3 10
1 2 3 4 5 6 7 8 9 10
output
4
思路
樸素的即可。雖然數據規模很小,但還是可以剪枝去掉一些情況。
- 超過個數去掉
- 總數超過去掉
最壞複雜度
總結
簽到題
代碼
#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 - 區間選點
題意
數軸上有 個閉區間 。取儘量少的點,使得每個區間內都至少有一個點(不同區間內含的點可以是同一個)。其中,,
input
3
1 3
2 5
4 6
output
3
1 3
2 5
4 6
思路
以線段右端點爲序,從小到大排序。如果當前線段沒被覆蓋過。則將用頂點將該線段覆蓋,同時標記其他左端點在該線段的線段爲覆蓋。
複雜度是,因爲每個線段只會被選擇一次。
正確性證明
由於以右端點爲序,對第條線段。若改線段未被覆蓋。那麼將點防止在處一定最優。因爲對於任意其它線段,線段的左端點一定要麼在的左邊,要麼在$ b_i$的右邊。
- 若在$ b_jb_i$處顯然可以覆蓋更多線段
- 若在右邊,放在那裏都不會影響該線段。
假設當前有一個點沒有放在線段右頂點處,考慮將點重新放置於有頂點處不會使結果更差。
總結
一道入門貪心題,可用交換論證證明,即我的決策不會令答案更差
代碼
#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 - 區間覆蓋
題意
數軸上有 ,個閉區間$ [ai, bi]$,選擇儘量少的區間覆蓋一條指定線段 。
覆蓋整點,即可以覆蓋。
不可能辦到輸出
input
3 10
1 7
3 6
6 10
output
2
思路
講區間以左端點爲序,從小到大排序。對於一段未被覆蓋的區間嗎,選取前的超過最多的線段,設選擇線段爲(假設 ,若 則覆蓋完成得到答案)。仍未被覆蓋的區間變爲,重複上述過程,直到,若掃描完所有線段仍未覆蓋完所有區間,則輸出。
複雜度爲
小技巧
上述過程的複雜度可以達到爲,方法是排序後。標記一個flag爲仍未被覆蓋的線段的首屆點,順序掃描數組,對尾節點取,知道線段首屆點超過。(因爲我們並不關心選取的具體是哪一條線段)。
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,錯誤的願意是選擇區間後,未覆蓋的區間應該變爲,而不是
#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;
}