程序设计思维与实践 Week3 作业 (3/4/数据班)

A 选数问题

题目

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

Examples

Input
1
10 3 10
1 2 3 4 5 6 7 8 9 10
Output
4

解题思路

通过DFS实现,但DFS的特性可能会导致时间复杂度过高或者无解的情况,所以我们可以通过设置条件对搜索过程进行剪枝:
(1)选的数的个数超过了K
(2)选的数的和超过了sum

实现代码

#include<iostream>
#include<cstdio>
using namespace std;
int a[105];
int t,n,k,s;
int res=0;
void dfs(int i,int sum,int count)
{
	if(sum>s||count>k) return;//剪枝 
	if(count==k)
	{
		if(sum==s)
		{
			res++;
			return;
		}
		return;
	}
	if(count<k)
	{
		for(int m=i;m<n;m++)
			dfs(m+1,sum+a[m],count+1);
	}


}
int main()
{
	cin>>t;
	for(int i=0;i<t;i++)
	{
		res=0;
		cin>>n>>k>>s;
		for(int j=0;j<n;j++)
			cin>>a[j];
		dfs(0,0,0);
		cout<<res<<endl;
	}
	return 0;
 } 

总结

通过剪枝来降低时间复杂度

B 区间选点

题目

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

Input

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

Output

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

Examples

Input
2
1 5
4 6
Output
1
Input
3
1 3
2 5
4 6
Output
2

思路

一道贪心的经典题目,我们总是希望使用尽可能少的点覆盖更多的区间,因此,贪心策略为:首先将所有区间按右端点升序排序,然后遍历区间,每次选择未覆盖区间的右端点进行覆盖。

贪心算法的证明

在这里插入图片描述

代码实现

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
struct item{
	int left;
	int right;
	bool operator < (const item &a)const
	{
		if(right!=a.right) return right<a.right;
		else return left>a.left;
	}
}p[105];
int main()
{
	int n;
	cin>>n;
	for(int i=0;i<n;i++)
	{
		cin>>p[i].left>>p[i].right;
	}
	sort(p,p+n);
	int now=p[0].right;
	int num=1;
	for(int i=1;i<n;i++)
	{
		if(p[i].left>now)
		{
			now=p[i].right;
			num++;
		}
	}
	cout<<num<<endl;
	return 0;
 } 

总结

贪心算法的使用容易出错,在使用前需要大致证明算法的正确性(局部最优→全局最优)

C 区间覆盖

题目

数轴上有 n (1<=n<=25000)个闭区间 [ai, bi],选择尽量少的区间覆盖一条指定线段 [1, t]
( 1<=t<=1,000,000)覆盖整点,即(1,2)+(3,4)可以覆盖(1,4)。
不可能办到输出-1

Input

第一行:N和T
第二行至N+1行: 每一行一个闭区间。

Output

选择的区间的数目,不可能办到输出-1

Examples

Input
3 10
1 7
3 6
6 10
Output
2

思路

(1)对区间进行预处理,将在[s,t]之外的部分都切掉
(2)按区间的左端点升序排序,若第一个区间的左端点不是s,则无解;若是,继续下一步
(3)依次遍历所有区间,若当前区间的左端点比现有的起始点小,则证明当前的区间已经覆盖了起始点,选择右端点更大的进行更新;若当前区间的左端点比现有的起始点大,则判断现在的终点能否和该区间连接上,若连接不上,则说明有区间未被覆盖,无解;若能连接上,则选择右端点更大的进行更新
(4)当更新后的终点≥t时,输出结果

实现代码

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
struct item{
	int left;
	int right;
	bool operator < (const item &a)const
	{
		if(left!=a.left) return left<a.left;
	}
}p[25003];
int main()
{
	int n,t;
	scanf("%d%d",&n,&t);
	for(int i=0;i<n;i++)
	{
		scanf("%d%d",&p[i].left,&p[i].right);
		if(p[i].left<1) p[i].left=1;
		if(p[i].right>t) p[i].right=t;
	}
	sort(p,p+n);
	if(p[0].left!=1)
	{
		printf("-1\n");
		return 0;
	}
	int count=1;
	int start=1,last=1;
	int i=0;
	bool judge=false;
	for(int i=0;i<n;i++)
	{
		if(p[i].left<=start)
		{
			last=max(p[i].right,last);
		}
		else
		{
			count++;
			start=last+1;//记得加一!!!
			if(p[i].left<=start)
			{
				last=max(p[i].right,last);
			}
			else break;
		}
		if(last>=t)
		{
			judge=true;
			break;
		}
	}
	if(judge!=true) printf("-1\n");
	else printf("%d\n",count);
	return 0;
 }

总结

(1)看清题目要求,整数覆盖!!!(在更新起始点时+1)(ㄒoㄒ)WA了好多次,不认真看题的后果
(2)读入的数据量较大,建议使用scanf读入
(3)TLE的处理:对数据进行剪枝操作(排序后起点不为s,[s,t]之外的区间),降低时间复杂度

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值