A - 选数问题
Given n positive numbers, ZJM can select exactly K of them that sums to S. Now ZJM wonders how many ways to get it!
Input
The first line, an integer T<=100, indicates the number of test cases. For each case, there are two lines. The first line, three integers indicate n, K and S. The second line, n integers indicate the positive numbers.
Output
For each case, an integer indicate the answer in a independent line.
Example
Input
1
10 3 10
1 2 3 4 5 6 7 8 9 10
Output
4
Note
Remember that k<=n<=16 and all numbers can be stored in 32-bit integer
问题分析
题意是数列中选出K个数,让它们的和等于S。
01背包问题,每个数都有被选中和没被选中两种状态。使用递归的思路,如果一个数加入已经选好的数之后,总数不超过K,且和仍然小于等于S,这个数就可以被选,然后继续选判断它之后的数。
为了方便,把选中的数加入一个列表,然后,递归的时候,下次要求的和就应该减去这个数,下次开始的位置是这个数之后相邻的数。
#include <iostream>
#include <list>
using namespace std;
int tot = 0;
void selectNumbers(int i, int* arr, int n, int K, int sum, list<int>& res)
{
if (res.size() == K && sum == 0)
{
tot++;
return;
}
if (i >= n)
return;
if (res.size() > K || sum < 0)
return;
selectNumbers(i + 1, arr, n, K, sum, res);
res.push_back(arr[i]);
selectNumbers(i + 1, arr, n, K, sum - arr[i], res);
res.pop_back();
}
int main()
{
int* arr = new int[10];
for (int i = 0; i < 10; ++i)
arr[i] = 0;
int T = 0, K = 0, S = 0;
cin >> T;
int a = 0, n = 0;
for (int i = 0; i < T; ++i)
{
cin >> n >> K >> S;
for (int j = 0; j < n; ++j)
cin >> arr[j];
tot = 0;
list<int> res;
selectNumbers(0, arr, n, K, S, res);
cout << tot << endl;
}
delete[]arr;
return 0;
}
B - 区间选点
数轴上有 n 个闭区间 [a_i, b_i]。取尽量少的点,使得每个区间内都至少有一个点(不同区间内含的点可以是同一个)
Input
第一行1个整数N(N<=100)
第2~N+1行,每行两个整数a,b(a,b<=100)
Output
一个整数,代表选点的数目
Examples
Input
2
1 5
4 6
Output
1
Input
3
1 3
2 5
4 6
Output
2
问题分析
区间可用结构体表示,存放左右两端点。
首先把结构按照左端点较大的优先的原则,用sort()
排序。这样,如果取排名靠前左端点,则更有可能也在之后的区间内。记下当前区间左端点lastX
,如果大于下一个区间的右端点,可以不再取下个区间,继续考虑其后的。
第一次提交的时候,由于没更新lastX的值,导致出错
#include <cstdio>
#include <algorithm>
using namespace std;
struct Inteval
{
int x, y;
Inteval() :x(0), y(0) {
}
}I[105];
bool cmp(Inteval a, Inteval b)
{
if (a.x != b.x)
return a.x > b.x;
else
return a.y < b.y;
}
int main()
{
int n;
scanf("%d", &n);
for (int i = 0; i < n; ++i)
{
scanf("%d%d", &I[i].x, &I[i].y);
}
sort(I, I + n, cmp);
int ans = 1, lastX = I[0].x;
for (int i = 1; i < n; ++i)
{
if (I[i].y < lastX) {
lastX = I[i].x;
ans++;
}
}
printf("%d
", ans);
return 0;
}
C - 区间覆盖(不支持C++11)
描述
数轴上有 n (1<=n<=25000)个闭区间 [ai, bi],选择尽量少的区间覆盖一条指定线段 [1, t]( 1<=t<=1,000,000)。
覆盖整点,即(1,2)+(3,4)可以覆盖(1,4)。
不可能办到输出-1
输入
第一行:N和T
第二行至N+1行: 每一行一个闭区间。
输出
选择的区间的数目,不可能办到输出-1
样例输入
3 10
1 7
3 6
6 10
样例输出
2
提示
这道题输入数据很多,请用scanf而不是cin
问题分析
这道题比较复杂,前面提交了9次都没有过,之后参考了别人写的文章,才得以通过。写完已经有一段时间,原文链接遗失了
首先看输入,既然输入的区间起点、终点都是整数,那么右端点小于1
的和左端点大于t
的区间都不可能覆盖[1, t]
,可以排除在外。对于有部分在[1, t]
内的区间,不取外面的那一部分。
然后考察已经读入的区间,判断是否可以加入,如果可以,更新可覆盖的最大值,最后比较这个值和t
。
#include <cstdio>
#include <algorithm>
using namespace std;
struct Node {
int l, r;
bool operator<(const Node& node)const {
return l < node.l;
}
}temp[100010];
int main() {
int n, t;
scanf("%d%d", &n, &t);
for (int i = 1; i <= n; i++) {
int x, y;
scanf("%d%d", &x, &y);
if (y < 1 || x > t) continue;
if (x < 1) x = 1;
if (y > t) y = t;
temp[i].l = x; temp[i].r = y;
}
sort(temp + 1, temp + n + 1);
if (temp[1].l != 1)
{
printf("-1
");
return 0;
}
int s = 1;
int ans = 0;
int i = 1;
while (i <= n && s <= t) {
int Max = -1;
if (temp[i].l > s) break;
while (i <= n && temp[i].l <= s) {
Max = max(Max, temp[i].r);
i++;
}
if (Max != -1) {
s = Max + 1;
ans++;
}
}
if (s <= t)
ans = -1;
printf("%d
", ans);
return 0;
}