「程序設計思維與實踐」Week3 作業:A - 選數問題、B - 區間選點、C - 區間覆蓋。dfs枚舉+貪心

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.

Sample Input

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

Sample Output

4

Hint

座標(x, y)表示第x行第y列,行、列的編號從0開始,且以左上角爲原點。
另外注意,輸出中分隔座標的逗號後面應當有一個空格。

題解

  • 考慮到數據範圍爲16,100組考慮dfs枚舉選數情況,並加少許剪枝。
  • dfs思路:從第一個數開始枚舉,記錄三個變量:當前枚舉到第幾個、當前已經選了幾個數、當前和,如果選數個數和當前和滿足條件那麼計數器加一,否則繼續從這個數後面的數字開始枚舉。
  • 剪枝:如果當前和已經大於題目要求的和,不再繼續搜素。

代碼

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

int K, S, n, ans = 0;
int nums[20];

bool vis[2333];
void dfs(int x, int cnt, int sum) {
	if (sum > S || cnt > K) return;
	if (sum == S && cnt == K) {
		ans ++;
		return;
	}
	for (int i = x+1; i <= n; ++i) {
		dfs(i,cnt+1,sum+nums[i]);
	}
}

int main() {
	int T;
	cin >> T;
	while (T--) {
		cin >> n >> K >> S;
		memset(nums, 0, sizeof(nums));
		memset(vis, 0, sizeof(vis));
		for (int i = 1; i <= n; ++i) {
			cin >> nums[i];
		}
		ans = 0;
		dfs(0,0,0);
		cout << ans << endl;
	}

	return 0;
}

B - 區間選點

題目描述

數軸上有 n 個閉區間 [a_i, b_i]。取儘量少的點,使得每個區間內都至少有一個點(不同區間內含的點可以是同一個)

input

第一行1個整數N(N<=100)
第2~N+1行,每行兩個整數a,b(a,b<=100)

Output

一個整數,代表選點的數目

Sample Input1

2
1 5
4 6

Sample Output1

1

Sample Input2

3
1 3
2 5
4 6

Sample Output2

2

題解

  • 貪心策略:先按照右端點從小到大排序,對於每個區間,如果沒有選過點,那麼就選區間最右端的點。
  • 合理解釋:對於一個區間,最右側的點覆蓋的其他區間個數一定大於等於其左側的點覆蓋的其他區間個數。

代碼

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

struct P {
	int a;
	int b;
}p[233];

int main() {
	int n;
	cin >> n;
	for (int i = 1; i <= n; ++i) {
		cin >> p[i].a >> p[i].b;
	}
	sort(p+1,p+n+1,[](P x, P y){return x.b < y.b;});
	int ans = 0, now = 0;
	for (int i = 1; i <= n; ++i) {
		if(now < p[i].a) {
			ans ++;
			now = p[i].b;
		}
	}
	cout << ans << 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

Sample Input1

3 10
1 7
3 6
6 10

Sample Output1

2

Hint

 這道題輸入數據很多,請用scanf而不是cin

題解

  • 貪心策略:對於如何選下一個區間:進行按照左端點排序,從當前點右側合法點中選取區間右端點最大的,這樣能夠保證選這一個區間的價值最大

代碼

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int MAXN = 25005;
struct P {
	int a;
	int b;
}p[MAXN];

bool cmp(const P &x, const P &y) {
	if(x.a != y.a) return x.a < y.a;
	return x.b > y.b;
}

int main() {
	int n, t;
	cin >> n >> t;
	for (int i = 1; i <= n; ++i) {
		scanf("%d%d", &p[i].a, &p[i].b);
	}
	sort(p+1,p+n+1,cmp);
	// puts("sorted:");
	// for (int i = 1; i <= n; ++i) {
	// 	cout << p[i].a << "," << p[i].b << endl;
	// }
	// puts("sorted end");
	int anscnt = 0; // ans
	int now = 0, i = 1; // now_cnt ,,, now,,,id
	while(now < t) { // another interval is needed
		// cout << now << ",,," << endl;
		int maxx = -1, maxid = -1;
		for (; i <= n; i++) {
			// cout << ">>" << p[i].a << endl;
			if(p[i].a > now+1) { //this interval begin cnnt connect
				break;
			}
			// then new.a <= now+1, so find the biggest one
			if(p[i].b > maxx) {
				maxx = p[i].b;
				maxid = i;
			}
		}
		if(maxid == -1) {
			puts("-1");
			exit(0);
		}
		now = maxx;
		anscnt++;
	}
	cout << anscnt << endl;


	return 0;
}


/*
5 10
2 7
3 4
5 6
3 6
6 10

*/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章