A - 选数问题
题意:
给定n个正数,从中选出K个使这些数的和为S,一共有多少种选法。
思路:
这道题首先使用DFS深度优先搜索,也就是从起点开始向后探索,一旦发现原来的选择不符合要求,就回溯重新选择另外一点,反复探索,求得选中的K所有和为S的个数。
这就是说从第一个数开始,遍历出所有的数,每个数都有选中和未选中两种情况,需要利用递归来解决。在递归的时候剪枝,就能够大大降低算法的复杂度。当判断出和为S的时候,个数就+1。一直到遍历完所有的选择或者选中的个数>K的时候就会进行剪枝。
代码:
#include<iostream>
#include<list>
#include<string>
#include<algorithm>
using namespace std;
#define _for(i,a,b) for(int i=a;i<b;i++)
int T, n, K, S;
int m[16];
list<int> res;
int step=0;
void solve(int i, int K,int sum)
{
if (res.size() == K && sum == 0)
{
step++;
return;
}
if (i >= n) return;
if (res.size() > K || sum < 0) return;
solve(i + 1, K, sum); //选中,放入链表中
res.push_back(m[i]);
solve(i + 1, K, sum - m[i]); //不选
res.pop_back(); //不选的话,将该数从链表里弹出删除
}
int main()
{
cin >> T;
_for(i, 0, T)
{
cin >> n >> K >> S;
_for(j, 0, n)
{
cin >> m[j];
}
solve(0, K, S);
cout << step << endl;
step = 0;
}
return 0;
}
B - 区间选点
题意:
数轴上有 n 个闭区间 [a_i, b_i]。取尽量少的点,使得每个区间内都至少有一个点(不同区间内含的点可以是同一个)
Input:
第一行1个整数N(N<=100)
第2~N+1行,每行两个整数a,b(a,b<=100)
Output:
一个整数,代表选点的数目
思路:
首先需要找到贪心指标,可以看到应该逐渐判断右端点值,就能完成我们需要的目的。那就应该将所有区间依据右端点b从小到大排序,从排序好的第一个区间右端点为最大右端点同时开始判断,如果第一个区间的右端点小于后一个区间的左端点,那么就必须多取一个点,将该区间的右端点覆盖最大右端点,在进行下一个区间的判断。如果大于等于,就说明该右端点在该区间里,再利用当前右端点再对下一个区间的左端点进行比较,直到比较完所有的区间。所统计来的数目就是我们想要的值。
左右区间可以用结构体来表示。
因为一开始考虑的不严谨,使得判断第一个区间的右端点小于等于后一个区间的左端点时频繁wa,一些小的细节也仍需注意。
代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
using namespace std;
#define _for(i, a, b) for(int i=a;i<b;i++)
struct section
{
int a;
int b;
bool operator < (const section& s)const
{
if(b!= s.b)
return b <s.b ;
else
return a > s.a;
}
}s[1000];
bool cmp(const int &s1 ,const int &s2)
{
return s1>s2 ;
}
int main()
{
int N;
cin >> N;
int end=-1;
int count = 0;
_for(i, 0, N)
{
cin >> s[i].a >> s[i].b;
}
sort(s, s + N);
_for(i, 0, N)
{
if (end< s[i].a)
{
end=s[i].b;
count++;
}
}
cout << count << endl;
return 0;
}
C - 区间覆盖
题意:
数轴上有 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。
思路:
这道题疯狂打脸,稍微看了一下题就去做,未能考虑(1,2)(3,4)可以覆盖(1,4)这种情况,以致于频繁的wa。虽然试了很多样例,但还是错误,求助于同学才给我指点出来。读题一定要细心细心细心。
区间利用结构体表示。
首先还是需要找到合适的贪心指标,可以看到区间从1-t,如果所有区间的起点不是1则不可能办到。也就是将闭区间左端点从小到大排序,然后对所有区间进行遍历,选择起点为1的最长区间,将其右端点+1 设置成起点,然后重复以上过程及就能得到想要结果。
代码:
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
#define _for(i,a,b) for(int i=a;i<b;i++)
struct section
{
int a;
int b;
bool operator <(const section &s1)
{
if(a!=s1.a) return a<s1.a;
else return b>s1.b;
}
}s[25001];
int main()
{
int N, T,a,b;
int count=1; //计数所用
scanf("%d %d", &N, &T);
_for(i, 0, N)
{
scanf("%d %d", &a, &b);
if(a<1) s[i].a=1; //防止输入错误情况(考虑多了)
else s[i].a=a;
if(b>T) s[i].b=T;
else s[i].b=b;
}
sort(s, s+ N);
int maxb=s[0].b;
int start=1; //起点设置为1
if(s[0].a!=1) //第一个区间起点不是1,则无解
{
cout<<-1<<endl;
return 0;
}
_for(i,0,N)
{
if(s[i].a<=start) //左端点小于等于起始点
maxb=max(maxb,s[i].b); //选择最大的右端点
else
{
count++; //否则那就需要多取一个区间
start=maxb+1; //将起点设置为最大右端点+1
if(s[i].a<=start)
maxb=s[i].b;
else
{
cout<<-1<<endl;
return 0;
}
}
if(maxb==T) //到达T
{
cout<<count<<endl;
return 0;
}
}
cout<<-1<<endl;
return 0;
}