**
A-选数问题
**
题目
给定n个正整数,要求选出K个数,使得选出来的K个数的和为sum,计算有多少种方案。
Input
第一行输入一个整数T,(T<=100),表示测试数据组数。每一组测试数据包含两行。第一行输入三个正整数:n,K,S;第二行为n个正整数。
Output
对于每组测试数据都在单独一行中输出一个正整数,表示方案个数。
Example
input
1
10 3 10
1 2 3 4 5 6 7 8 9 10
output
4
注意
K<=n<=16,且所有整数都可以用32位整型数据表示。
解题思路
利用递归调用,先将n个整数存入数组中,从数组的第一个元素开始递归调用,每一个元素有两个选择:不被选入K个数中,就继续进入下一个递归调用;选入K个数中,就将其插入到链表中,将其从sum总数中减去,继续进行下一层的递归调用。当传入的元素索引大于总数n、链表元素超过K个或sum小于0时,返回上一层函数;若链表中元素个数为K,且sum恰好减为0,则将方案总数加一。递归调用完成,就将所有可能的方案遍历完成。
C++代码
#include<iostream>
#include<list>
using namespace std;
list<int> res;//定义链表,用于判断选数个数
int result=0;//存放方案个数
int *a=new int[17];//数组,存放n个整数
void solve(int i,int sum,int k,int n,list<int> &res)
{//dfs递归调用函数
if(res.size()==k&&sum==0)//当选出K个数且其和为sum
{
result++;
return;
}
if(i>=n) return;
if(res.size()>k||sum<0) return;
solve(i+1,sum,k,n,res);//不选,直接进入下一次调用
res.push_back(a[i]);
solve(i+1,sum-a[i],k,n,res);//选择这个数,将其插入链表,再调用
res.pop_back();
}
int main()
{
int t;
cin>>t;
for(int i=0;i<t;i++)
{
result=0;
res.clear();//清空链表
int n=0,k=0,s=0;
cin>>n>>k>>s;
for(int j=0;j<n;j++)//将n个整数存入数组
{
cin>>a[j];
}
solve(0,s,k,n,res);//调用递归函数
cout<<result<<endl;//输出结果
for(int j=0;j<n;j++)//清空数组
{
a[j]=0;
}
}
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
解题思路
定义结构体存放区间的左右端点,将区间先按照右端点从小到大排序,再根据左端点从小到大排序。排序完成后,先选择第一个区间的右端点,判断该点与下一个区间的关系,找到下一个不包含该点的区间,再次选择下一个区间的右端点,以此类推,直到循环判断完所有的区间,得到最终的点数。
C++代码
#include<iostream>
#include<algorithm>
using namespace std;
struct area
{
int a,b;//区间的左右端点
bool operator<(const area &x) const
{//重载小于号,先根据区间的右端点排序,再根据左端点排序
if(b!=x.b) return b<x.b;
else if(a!=x.a) return a<x.a;
}
};
int main()
{
int n;
while(cin>>n)
{
area *m=new area[n];//定义结构体数组,存放区间
int sum=0;
for(int i=0;i<n;i++)//为数组赋值
{
cin>>m[i].a>>m[i].b;
}
sort(m,m+n);//将数组排序
for(int i=0;i<n;i++)
{
int point=m[i].b;//每次循环选择区间的右端点
sum++;//点数加一
while(point>=m[i+1].a)//找到不包含point点的下一个区间
{
i++;
}
}
cout<<sum<<endl;
delete [] m;
}
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
样例输入
3 10
1 7
3 6
6 10
样例输出
2
提示
这道题输入数据很多,请用scanf而不是cin
解题思路
定义结构体存放区间的左右端点,将n个区间根据左端点从小到大、右端点从大到小的顺序排序。对区间进行预处理,包含线段左端点的区间,将其在线段左侧的部分删去,即更新区间左端点为线段左端点,在更新后的区间中找到区间长度最大的区间,将线段的左端点更新为该区间的右端点加一,即只考虑线段在该区间右侧的部分,继续之前对区间的操作。如果找到的区间包含了线段的右端点,则完成对线段的覆盖,跳出循环,输出结果即可。如果在循环更新过程中,始终没有包含线段左端点或右端点的区间,则不可能办到,输出-1。
C++代码
#include<algorithm>
#include<stdio.h>
using namespace std;
struct area
{
int a,b;//区间的左右端点
bool operator<(const area &x) const
{//重载小于号,选根据区间左端点排序
if(a!=x.a) return a<x.a;
else if(b!=x.b) return b>x.b;
}
};
int main()
{
int n=0,t=0;
scanf("%d%d",&n,&t);
area line;//线段区间
area *m=new area[25002];//存放n个区间
line.a=1,line.b=t;
for(int i=0;i<n;i++)
{
scanf("%d%d",&m[i].a,&m[i].b);
}
sort(m,m+n);//将区间数组排序,先按照左端点从小到大排,再根据右端点从大到小
int count=0;
int maxlenth=0,maxid=0;//记录每次循环找到的当前区间最大长度,和其在数组中的索引
while(true)
{
maxlenth=0;
for(int i=0;i<n;i++)//对区间进行预处理,去掉线段左边的区间长度
{
if(m[i].a<=line.a&&m[i].b>=line.a)
{
m[i].a=line.a;//更新区间的左端点
if(m[i].b-m[i].a>=maxlenth)//找到长度最长的区间
{
maxlenth=m[i].b-m[i].a;
maxid=i;
}
}
else if(m[i].a>line.a) break;//遇到左端点大于线段左端点的区间,停止预处理
}
if(m[maxid].a!=line.a)//如果没有包含线段左端点的区间,就说明不可能办到
{
printf("-1\n");
return 0;
}
line.a=m[maxid].b+1;//存在这样的区间,就将线段区间的左端点更新为该区间右端点的下一个整数
count++;//找到的区间个数加一
if(m[maxid].b>=line.b) break;//如果线段右端点包含在该区间内,则可以停止寻找
if(maxid==n-1)//如果遍历完所有区间都没有包含线段右端点的区间,则说明不可能找到
{
printf("-1\n");
return 0;
}
}
printf("%d\n",count);
return 0;
}