程序设计思维 week3

选数问题(DFS)

题意

iven 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.

做法

对于每一个数有选择和不选择两种情况。
因此,通过递归,对于数据分叉成选和不选两种情况。
对于不合法的情况:总数目>K,遍历位置>n,总和>sum的情况及时return;

代码

#include<iostream>
#include<vector>
using namespace std;
int T,n,K,S;
int a[900],ans=0;
vector<int> res;
void dfs(int x,int sum)
{
	if(res.size()==K&&sum==0)
	{
		ans++;
		return;
	}
	
	
	if(x>n)return;
	if(res.size()>K||sum<0)return;
	dfs(x+1,sum);
	res.push_back(a[x]);
	dfs(x+1,sum-a[x]);
	res.pop_back();
	
}
int main()
{

	cin>>T;
	for(int i=1;i<=T;i++)
	{	ans=0;
		res.clear() ;
		cin>>n>>K>>S;
		for(int j=1;j<=n;j++)cin>>a[j];
		dfs(1,S);
		cout<<ans<<endl;
	}
 }

区间选点(贪心)

题意

数轴上有 n 个闭区间 [a_i, b_i]。取尽量少的点,使得每个区间内都至少有一个点(不同区间内含的点可以是同一个)

Input
第一行1个整数N(N<=100)
第2~N+1行,每行两个整数a,b(a,b<=100)

Output
一个整数,代表选点的数目

做法

直观印象是将所有线段在数轴上排开,将重合区域最多的位置所包含的线段去掉,结果++;直到所有线段都被选中。
以上跟线段顺序无关系。
我们可以考虑加一个条件:按照某种规则看是否应该选点。
规则:将所有线段按照右端点大小顺序排列,记录pos为已选边的最右断点。已选边从左到右遍历第i条线段,如果第i条线段的左端点位置>pos,则更新pos,结果++;
贪心最优性证明
按上述规则排布好线段后,对于一条线段,肯定是选点越靠近右,与此点所落到的线段数目越多。故每次选线段右端点,去除已选线段后,又出现类似子结构。

代码

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
struct qj{
	int a,b;
	bool operator <(const qj &p)const
{
	if(b!=p.b)return b<p.b;
}
};
qj  mm[110];
int main()
{
	
	int N;
	cin>>N;
	for(int i=1;i<=N;i++)
	{
		cin>>mm[i].a>>mm[i].b;
	}
	sort(mm+1,mm+N+1);
	int ans=0;
	int pos=-1;
	for(int i=1;i<=N;i++)
	{
		if(mm[i].a>pos)
		{
			ans++;
			pos=mm[i].b;
		}
	}
	cout<<ans<<endl;
	
}

区间覆盖(贪心)

题意

描述
数轴上有 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即可。
因此,首先按照左端点大小排序,依次向后遍历更新待覆盖线段的左端点为小于原待覆盖节点左端点的线段的最大右端点。

贪心最优性证明:
设用贪心算法算出来的结果为 ans;
若最优结果 为best;
注意到选边的顺序跟最终结果没有关系,那不妨还是按照前述已经sort完成的顺序,从左往右选边。
在对于任意一次选择,若选了一个不具有最大右端点的线段作为覆盖,则下次选择必然要弥补上这块差距区域,因此结果只能更差。
而贪心每次都是选的最右端。
因此归纳得出,best=ans;

代码

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
struct qj{
	int a,b;
	bool operator <(const qj &p)const
{
	if(b!=p.a)return a<p.a;
}
};
qj  mm[66000];
int main()
{
	
	int N,T;
	scanf("%d %d",&N,&T);
	for(int i=1;i<=N;i++)
	{
		scanf("%d %d",&mm[i].a,&mm[i].b);
	}
	sort(mm+1,mm+N+1);
	int ans=0;
	int pos=0,max=0;
	for(int i=1;i<=N;i++)
	{	int count=0;
		while(mm[i].a<=pos+1&&i<=N)
		{
			if(mm[i].b>max)
			{
				count++;
				max=mm[i].b;
			}
			i++;
		}
		i--;
		pos=max;
		if(count>0)ans++;
		else break; 
	}
	if(pos<T)printf("-1\n");
	else printf("%d\n",ans);
	
}

总结

1、猝死在下述样例。得出教训:更新条件是 if(mm[i].b>max)

input
2 11
1 11
2 11
output
1

2、数组开太小会直接tle或wa

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值