A - 选数问题

题意

给定nn个数,从中选取kk个,另总和为ss,共TT组数据。其中kn16k \le n \le 16T100T \le 100

input

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

output

1
4

思路

朴素的dfsdfs即可。虽然数据规模很小,但还是可以剪枝去掉一些情况。

  • 超过kk个数去掉
  • 总数超过ss去掉

最坏复杂度O(n!)O (n!)

总结

签到题

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#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

1
2
3
4
3
1 3
2 5
4 6

output

1
2
3
4
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$的右边。

  • 若在bjb_j左边,放在bib_i处显然可以覆盖更多线段
  • 若在bjb_j右边,放在那里都不会影响该线段。

假设当前有一个点没有放在线段右顶点处,考虑将点重新放置于有顶点处不会使结果更差。

总结

一道入门贪心题,可用交换论证证明,即我的决策不会令答案更差

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#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

1
2
3
4
3 10
1 7
3 6
6 10

output

1
2
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。(因为我们并不关心选取的具体是哪一条线段)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#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;
}