程序设计思维作业 week3
本周主要练习的内容是贪心算法,主要的找到贪心准则。整体做题体验前两题非常顺利,每道题20分钟左右,第三题在降低复杂度方面花费了较多时间。
Problem A 选数问题
Given N positive numbers, you should select K of them that sum to S. Now please calculate how many ways to get it!
1.sample input and output
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.
1
10 3 10
1 2 3 4 5 6 7 8 9 10
Output
For each case, an integer indicate the answer in a independent line.
4
2.整体思路及代码
对输入数组进行遍历,对于每一个元素都有选和不选两个选项。如果选,用与目标和的差值减去该元素,然后看下一个元素。如果不选,直接进入下一个元素。其中及时终止条件(可行性剪枝)是与目标和的差值小于0,或选取数的数目超过K个却还不到目标和S。
#include<iostream>
#include<algorithm>
using namespace std;
int tmp;
int n,m,K,S;
int *p;
void SOL(int i,int size,int sum)
{
//符合K个数相加等于S的要求
if(sum==0&&size==K)
{
tmp++;
return ;
}
//提前结束情况
if(sum<0||size>K||i>=m)
return ;
//如果选第i个数
SOL(i+1,size+1,sum-p[i]);
//不选
SOL(i+1,size,sum);
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
tmp=0;
//一共m个数,K个数的和为S的方案个数
cin>>m;
cin>>K;
cin>>S;
p=new int[m];
for(int j=0;j<m;j++)
{
cin>>p[j];
}
SOL(0,0,S);
cout<<tmp<<endl;
}
}
Problem B 区间选点问题
数轴上有 n 个闭区间 [a_i, b_i]。取尽量少的点,使得每个区间内都至少有一个点(不同区间内含的点可以是同一个)
1.sample input and output
Input
第一行1个整数N(N<=100)
第2~N+1行,每行两个整数a,b(a,b<=100)
2
1 5
4 6
Output
一个整数,代表选点的数目
1
2.整体思路及代码
经典贪心问题。先将输入的区间按右端点由小到大排序,用lim记录当前选中区间的右端点值。然后遍历区间数组,如果lim点在当前区间中,continue到下一区间。如果不在,则将该区间右端点赋值给lim,并选取区间数++。
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
class section
{
public:
section()
{
_a=0;
_b=0;
}
section(int a,int b)
{
_a=a;
_b=b;
}
inline bool operator < ( section & x)
{
return _b<x._b;
}
public:
int _a,_b;
};
bool cmp(section a,section b)
{
return a._b<b._b;
}
int n;
vector<section> p;
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
int a,b;
cin>>a;
cin>>b;
section Nsection(a,b);
p.push_back(Nsection);
}
sort(p.begin(),p.begin()+n,cmp);
int lim=p.front()._b;
int tmp=1;
for(int i=1;i<n;i++)
{
if(lim>=p[i]._a&&lim<=p[i]._b)
continue;
else
{
tmp++;
lim=p[i]._b;
}
}
cout<<tmp<<endl;
return 0;
}
Problem C 区间覆盖问题
数轴上有 n (1<=n<=25000)个闭区间 [ai, bi],选择尽量少的区间覆盖一条指定线段 [1, t]( 1<=t<=1,000,000)。
覆盖整点,即(1,2)+(3,4)可以覆盖(1,4)。
不可能办到输出-1
1.sample input and output
Input
第一行:N和T
第二行至N+1行: 每一行一个闭区间。
3 10
1 7
3 6
6 10
Output
选择的区间的数目,不可能办到输出-1
2
2.整体思路及代码
贪心问题,先上一张解题心路历程图。
思路设计方面,先将输入的区间数组按照右端点排序。用mlim来记录当前选中区间的最右端点数值,用pos来记录当前选中区间在数组中的位置。每次调用count函数,会从pos往后遍历数组,选取从milm开始,并且右端点值最大的区间,将新的右端点值赋给Mlim,记录pos。直到到达目标右端点值t。其中,无法实现的情况有,区间与区间之间有无法覆盖的空点(体现在代码中是Mlim没有被赋予新值),最大右端点无法到达目标值t(体现在代码中是start等于Mlim)。
最终代码复杂度为O(nlogn)
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
class section
{
public:
section()
{
_a=0;
_b=0;
}
section(int a,int b)
{
_a=a;
_b=b;
}
public:
int _a,_b;
};
int n;//区间个数
int t;//目标右端点值
int wa=-1;//错误输出
int tmp=1;//区间个数计数
vector<section> p;//区间数组
bool cmp(section a,section b)
{
return a._b<b._b;
}
void count(int s,int start)
{
//记录从start开始的区间能够到达的最右点
int Mlim=0;
//记录下一次数组遍历的起始位置
int pos=0;
//判断是否成功结束
if(start==t)
{
cout<<tmp;
return;
}
//简化区间到【start,t】之中
for(int i=s+1;i<p.size();i++)
{
if(p[i]._a<=start+1)
{
//p[i]._a = start;
if(p[i]._b>Mlim)
{
Mlim = p[i]._b;
pos=i;
}
}
}
if(Mlim==0||Mlim==start)
{
cout<<wa;
return ;
}
else {
tmp++;
//cout<<Mlim<<endl;
count(pos,Mlim);
}
}
int main()
{
while(cin>>n)
{
scanf("%d", &t);
//记录从1起始的区间能够到达的右极点
tmp=1;
int mlim = 0;
for (int i = 0; i < n; i++) {
int a, b;
scanf("%d", &a);
scanf("%d", &b);
if (b >= t) {
b = t;
}
if (a <= 1) {
a = 1;
if (b > mlim)
mlim = b;
}
section Nsection(a, b);
p.push_back(Nsection);
}
if (mlim == 0) {
cout << wa;
return 0;
}
//按右端点排序
sort(p.begin(), p.begin() + p.size(), cmp);
count(0, mlim);
p.clear();
}
return 0;
}