Problem 1 选数问题
问题描述
在n个数中,选k个数使得和恰好为sum,有多少种组成的结果(原题懒得翻译过多,总的来说就是这么个意思)
输入
第一行:T表示会有多少个测试样例
第二行:n,K,S表示从n个数中,选K个数,使得结果为S
第三行:n个数
输出
输出可组成的方案数
问题分析
1.n个数中选k个,这道题很明显就涉及到了搜索的知识。这里不需要求最佳解,于是宽搜深搜都可以,只是需要做好优化。
2.我这里采用的是深搜,首先先对存放那n个数的数组按升序的顺序进行一个排序。然后就调用函数开始执行深搜。由于是有序的,所以在加法进行到第q个数的时候(q<k),只要这前q个数选上的数的和已经大于我们要求的sum,那么这个状态就是不可取的,这条路就可以被pass掉。
3.在我的函数里面,addition(int layer,int sum,int i)
分别代表了选了几个数,离目标状态还差多少,以及现在是对第几个数进行操作。
代码
#include<iostream>
using namespace std;
#include<algorithm>
int shu[20];
int k,s,n;
int step;
bool cmp(int a,int b)
{
return a<b;
}
void `addition(int layer,int sum,int i)`//c层,剩下的数,第i个数的选择与否
{
if(layer==k)
{
if(sum==0)step++;
//cout<<"加一"<<endl;
return;
}
else
{
if(sum<0) return;
if(i>=n)return;
addition(layer+1,sum-shu[i],i+1);
addition(layer,sum,i+1);
//cout<<"过程1"<<endl;
}
}
int main()
{
int T;
cin>>T;
for(int i=0;i<T;i++)
{
cin>>n>>k>>s;//k个数,和为s
step=0;
for(int j=0;j<n;j++)
{
cin>>shu[j];
}
//cout<<"进入深搜前"<<endl;
sort(shu,shu+n,cmp);
addition(0,s,0);
//cout<<"搜完了"<<endl;
cout<<step<<endl;
}
}
遇到的问题
对于宽搜需要准备传参的数据没有提前规划好,只是想到啥就加上啥。导致后来还找了半天的错才发现是最开始引用addition函数时用的传进去的数据不对,导致调用的函数直接出去。
problem2——区间选点
问题描述
数轴上有 n 个闭区间 [a_i, b_i]。取尽量少的点,使得每个区间内都至少有一个点(不同区间内含的点可以是同一个)
输入
第一行1个整数N(N<=100)
第2~N+1行,每行两个整数a,b(a,b<=100)
输出
一个整数,代表选点的数目
问题分析
1.总体上先来分析一波这个题的解体思路,换一种说法就是选最少的点使得区间全部都有至少一个的点。虽然区间是有限制的,但是一开始你对于要选多少个点是没有准确的值的。所以说遍历肯定不可取,花费的功夫太大了需要2^100次,所以我们必须得采用个节约运算次数的算法——贪心算法。
2. 首先,设立结构体section,分别储存一段区间的左右断点。
然后对sections按照右端点的大小来排序,当右端点一样时,再按照左端点的大小来排序。if(x.b!=y.b) return x.b<y.b;else return x.a>y.a;
3.然后从sections[0]开始,选其右端点为选中的点,按着顺序往后走,一旦有区间无法被这个点占用,就换成该区间的右端点。以此类推。
为何每个区间都能恰好包含上?
原因:假设现在选到了第m个区间上的点,此时上一个选中的顶点R是m区间的右端点。因为我们排序的大小首先是按照区间的右端点来排序的,所以m+1区间是不可能在R之前就结束了,即m+1区间的右端点一定在R的右边,于是m+1区间只会有包含R点和全部在R点右边两种情况,如果是第二种,两区间完全没有交集肯定得+1。第一种的话也很明显的可以采用同一个点,不需要再多加点。
代码
#include<iostream>
using namespace std;
#include<algorithm>
#include<vector>
struct section
{
int a;
int b;
};
bool cmp(section x,section y)
{
if(x.b!=y.b)
return x.b<y.b;
else
return x.a>y.a;
}
section sections[100];
int shu;
int minnum;
int N;
vector<int> q;
void setpoint()
{
int point=sections[0].b;
q.push_back(point);
for(int j=1;j<N;j++)
{
if(point<sections[j].a)
{
point=sections[j].b;
q.push_back(point);
}
}
}
int main()
{
int a,b;
shu=0;
minnum=10000;
cin>>N;
for(int i=0;i<N;i++)
{
cin>>sections[i].a>>sections[i].b;
}
sort(sections,sections+N,cmp);
setpoint();
cout<<q.size()<<endl;
//cout<<"选的数 "<<endl;
//vector<int>::iterator it;
//for(it=q.begin();it!=q.end();it++)
//cout<<*it<<endl;
return 0;
}
遇到的问题
最开始的排序不对,我一开始采用的是先以左端点排序,然后这个想法对待部分数据(比如样例)是一样的答案。然后我就找了半天的错误,才找到这个地方这么做要对一些。个人认为左端点排序更适合下面那道题一些,就是对于区间的范围很在意的题目,会好一些。
problem3——区间覆盖
问题描述
数轴上有 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.同上题一样的结构体section,记录区间左右端点。对sections进行排序,先按照左端点的大小来排序,再按照右端点进行排序。(a.left<b.left||(a.left==b.left&&a.right>b.right));
3.在挨个搜索时,先注意考虑特殊情况,比如第一个区间就能够覆盖整个线段。再比如,根本就没有左端点小于等于1的区间,又或者根本就没有比t大的右端点。这些情况直接对应结果即可。
4.我先做了一个初步的减小范围,就是先遍历一遍,把左端点大于t的区间排除出去。
5.然后,我用current来保存当前已经被覆盖的线段点。先把第一个区间加进去。当上一个加入的区间为m时,从m+1开始遍历,找到所有序号比m大,且左端点在current所保存的区间里。随后找到那个加入能使得current的右端点变化最大的区间。把它加进来,并对current更新。重复操作直到所有在范围内的点都被找完,或者到达t了。
代码
#include<iostream>
using namespace std;
#include<algorithm>
int N,T;
struct section
{
int left;
int right;
};
bool cmp(section a,section b)
{
return (a.left<b.left||(a.left==b.left&&a.right>b.right));
}
section sections[25000];
int main()
{
//cin>>N>>T;
scanf("%d%d",&N,&T);
for(int i=0;i<N;i++)
{
//cin>>sections[i].left>>sections[i].right;
scanf("%d%d",§ions[i].left,§ions[i].right);
}
sort(sections,sections+N,cmp);
int shu=N;
int step=1;
int maxm=1;
for(int i=0;i<N;i++) {
// 和目标线段有交集的点数,剪枝1
if(sections[i].left>T+1 )
{
shu=i;
break;
}
else
{
if(sections[i].right>maxm)
maxm=sections[i].right;
}
}
section current;//当前覆盖的左右交点;
if(sections[0].left!=1||maxm<T)
{
cout<<-1<<endl;
//cout<<sections[0].left<<endl;
//cout<<maxm<<endl;
return 0;
}
else
{
current.left=1;
current.right=sections[0].right;
if(current.right>=T)//第一个就满足条件
{
cout<<1<<endl;
return 0;
}
//已经弄到哪一个数了
int rright=0;//找到的最右边的点
int minus;//差值
int pan=0;
while(true)
{
minus=0;
int i;
for( i=rright;i<shu&§ions[i].left<=current.right+1;i++)//遍历所有与其有交点的
{
if(sections[i].right-current.right>minus)
{
minus=sections[i].right-current.right;
rright=i;
}
//cout<<i<<" "<<minus<<" current "<<current.right<<endl;
}
//cout<<i<<" "<<minus<<" current "<<current.right<<endl;
if(minus==0)//剩下的交点中没有可让current区间更新的小区间
{
break;
}
else
{
step++;
current.right=sections[rright].right;
//cout<<" 下一个是 "<<sections[rright].left<<" "<<sections[rright].right<<endl;
//cout<<"当前覆盖区间 "<<current.left<<" "<<current.right<<endl;
if(current.right>=T)
break;
}
}
if(current.right>=T)
cout<<step<<endl;
else
{
cout<<-1<<endl;
//cout<<" minus=" <<minus<<" current="<<current.right<<endl;
}
}
}
遇到的问题
一开始不太清楚思路,在网上找到了对于思路的文字描述,自己写起来却发现自己老是会漏掉一些情况没有考虑。像比如我的rright的初始化忘记做了。没有考虑到第一个区间就能够把线段覆盖完的情况。都是对于代码不熟悉的结果。