程序设计思维与实践 - Week3 习题

Problem A - 选数问题

Description

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.

Sample Input
1
10 3 10
1 2 3 4 5 6 7 8 9 10
Sample Output
4
Note
Remember that k<=n<=16 and all numbers can be stored in 32-bit integer
Idea

[DFS] 先定义一个变量t=0。对所有的数遍历,选是一种情况,不选是一种情况,然后分别向下一层搜索。满足条件的话,返回t++。如果选的数超过限度或者和大于输入值停止。最后所有的成功情况都会对t+1,得到的t值就是答案。

Codes
TimeMemoryLengthLangSubmitted
265ms140kB588GNU G++17 7.3.02020-03-05 14:30:27
#include<iostream>
#include <list>
#define _for(i,a,b) for(int i=(a);i<(b);i++)
using namespace std;
int List[16]={0}; 
int n,K,S;int t;
list<int> res;
void Gets(int i,int sum,list<int> &res){
	if(res.size()==K && sum==0){
	   t++; return;
	}
	if(i>=n) return;
	if(res.size()>K ||sum<0) return;
	Gets(i+1,sum,res);
	res.push_back(List[i]);
	Gets(i+1,sum-List[i],res);
	res.pop_back();
}
void getWays(int n,int K,int S){
	_for(i,0,n){
		cin>>List[i];
	}
	t=0;
    Gets(0,S,res);
	cout<<t<<endl;
}
int main(){
	int T;cin>>T;
	while(T--){
		cin>>n>>K>>S;
		getWays(n,K,S);
	}
	
	return 0;
} 

Problem B- 区间选点

Description

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

Input

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

Output

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

Input
2
1 5
4 6

Output

1

Input

3
1 3
2 5
4 6

Output

2
Idea

贪心策略: 按照<右端小的在前,右端一样的左端小的在前>规则,对输入的所有区间进行排序。然后从头遍历每个区间。初始sum=0,定义一个临时变量cnt作为循环中的右端点(初始为第一个区间右端点),每次观察下一个区间左端点是否与当前 cnt相交,不相交的话就sum+1(说明需要取新的点),并更新cnt为其右端点 (相交的话用当前的右端点就够了)。

Codes
TimeMemoryLengthLangSubmitted
15ms8kB814GNU G++17 7.3.0G++
#include<iostream>
#include <list>
#define _for(i,a,b) for(int i=(a);i<(b);i++)
using namespace std;
int sum=0,N;
struct Pair{
	int a,b;
	bool operator<(Pair X){
		if(X.b==b) return a<X.a;
		return b<X.b;
	}
}; Pair M[100];
void ShellSort(Pair A[],int n){
    int i, j,cnt;
	Pair tmp; 
    for (cnt = n/ 2; cnt > 0; cnt /= 2) {    
        for (i = cnt; i < n; i++) {  
            tmp = A[i];  
            for (j = i - cnt; j >= 0 && tmp < A[j]; j -= cnt) {  
                A[j + cnt] = A[j];  
            }  
            A[j + cnt] = tmp;
        }  
    }
}
void getSum(){
	_for(i,0,N){
		cin>>M[i].a>>M[i].b;
	}
	ShellSort(M,N);
	sum=1;
	int cnt=M[0].b;
	for(int i = 1; i < N; i++){
		if(M[i].a > cnt) {  
		cnt = M[i].b;
		sum++;
	    }
	}
	cout<<sum<<endl;
}
int main(){
	cin>>N;
	getSum();
	return 0;
}

Problem C - 区间覆盖

Description

数轴上有 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

Sample Input
3 10
1 7
3 6
6 10
Sample Output
2
Note

这道题输入数据很多,请用scanf而不是cin

Idea

贪心策略:先按左端点大小升序,再按右端点升序。这样即便有被包括的区间,小也会被放在前面,不影响更新—>把1作为初始Start(左端点)和End(右端点),每次从当前区间向后遍历,遇到最后一个左端点<start右端点>End的,把End作为当前End,Start取下一个点(End+1),Sum++,然后向后接着观察及更新。需要注意终止循环的边界条件。由于我用的for循环最后一步没法向后走了,所以需要在循环外加一句判断,如果End>T,就输出sum+1反之输出-1.

Codes
TimeMemoryLengthLangSubmitted
63ms836kB810G++2020-03-06 14:10:30
#include<cstdio>
#include<iostream>
#include<algorithm>
#define _for(i,a,b) for(int i=(a);i<(b);i++)
using namespace std;
const int MAX=1<<20;
struct Node{
	int a,b;
}; 
bool cmp(Node X,Node Y){
		if(X.a==Y.a) return X.b<Y.b;
		return X.a<Y.a;
	}
Node M[MAX]; int Start, End,N,T; bool Tag=true;
int main(){
	
	while(scanf("%d%d",&N,&T)!=EOF){
    int sum=0;Tag=true;
	_for(i,0,N){	 
	scanf("%d%d",&M[i].a, &M[i].b); 
	} 
	sort(M,M+N,cmp);
	if(M[0].a>1)  {  puts("-1"); continue; } 
	Start=1,End=1;  int nEnd=1;
    _for(i,0,N){
		if(M[i].a<=Start && M[i].b>End){
    		End=M[i].b;	
		}
        if(M[i].a>Start){
        	if(M[i].a>End+1) {
			 Tag=false;break;
			} 
			Start=End+1; sum++; i--; 
		}
		if(End>=T) break;
	}
	if(End<T ||Tag==false)   {  puts("-1");} 
	else printf("%d\n",sum+1);
	}
	return 0;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值