寒假预备役学习第三、四天

这两天大部分时间都在完成洛谷团队上的算法题,对于搜索这一类型的题目不再像之前一样没有头绪,能够有一些基本的想法和框架,能较快想到使用哪一种搜索(有些题目深搜广搜感觉都能做,但是目前还没有达到那个水平),但还是会经常卡住,有些地方想到了却不知道怎么用代码去实现,可能是不够熟练的原因导致思路时常打不开,怎样去更好的使用搜索,或者使用的更加灵活还是需要去多加练习。

今天进行了寒假预备役的第一次测试,敲了三道题,但是只有第一道题样例全过,自己一时半会也没找出问题来,之后抽空把题都补一下,看看是哪些细节没有注意到(学了几天的搜索发现前面的一些东西也忘记了很多,还是要多复习多巩固),接下来这一周里打算先继续写完洛谷团队里的题,然后周末把这周学的东西和写了的题目全部再看一看,整理一下思路。

目录

P2404 自然数的拆分问题

P1219 [USACO1.5] 八皇后 Checker Challenge(该问题是非常经典的搜索问题)

P2392 kkksc03考前临时抱佛脚

P2036 [COCI 2008/2009 #2] PERKET


P2404 自然数的拆分问题

题目描述

任何一个大于 11 的自然数 �n,总可以拆分成若干个小于 �n 的自然数之和。现在给你一个自然数 �n,要求你求出 �n 的拆分成一些数字的和。每个拆分后的序列中的数字从小到大排序。然后你需要输出这些序列,其中字典序小的序列需要优先输出。

输入格式

输入:待拆分的自然数 �n。

输出格式

输出:若干数的加法式子。

输入输出样例

输入 #1复制

7

输出 #1复制

1+1+1+1+1+1+1
1+1+1+1+1+2
1+1+1+1+3
1+1+1+2+2
1+1+1+4
1+1+2+3
1+1+5
1+2+2+2
1+2+4
1+3+3
1+6
2+2+3
2+5
3+4

说明/提示

数据保证,2≤�≤82≤n≤8。

思路(写了好久没写出来,看了视频讲解然后进行代码复现):

我们在这里采用dfs深搜进行,将n分步进行搜索,利用一个数组进行存数,每当步数为0且剩余长度>1时进行输出(输出答案不能为7,所以剩余长度必须>1),最难实现的是对数组进行存数和回溯的实现,我们利用一个循环,(此时刚刚开始存数)只要数组里的数还未存满且步数未走完就进行存数,同时进行回溯。

代码:

#include
using namespace std;
int n,len=0;
int path[30];
void dfs(int u) {
	if (u == 0&&len>1) {
		cout << path[1];
		for (int i = 2; i <= len; i++) {
			cout <<"+" << path[i];
		}
		cout << endl;
		return;
	}
	for (int i = path[len]; i <= u; i++) {
		if (u - i >= 0) {
			path[++len] = i;
			dfs(u - i);
			len--;
		}
	}
}
int main() {
	cin >> n;
	path[0] = 1;
	dfs(n);
	return 0;
}

P1219 [USACO1.5] 八皇后 Checker Challenge

题目描述(该问题是非常经典的搜索问题

一个如下的 6×66×6 的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。

寒假预备役学习第三、四天_第1张图片

上面的布局可以用序列 2 4 6 1 3 52 4 6 1 3 5 来描述,第 �i 个数字表示在第 �i 行的相应位置有一个棋子,如下:

行号 1 2 3 4 5 61 2 3 4 5 6

列号 2 4 6 1 3 52 4 6 1 3 5

这只是棋子放置的一个解。请编一个程序找出所有棋子放置的解。
并把它们以上面的序列方法输出,解按字典顺序排列。
请输出前 33 个解。最后一行是解的总个数。

输入格式

一行一个正整数 �n,表示棋盘是 �×�n×n 大小的。

输出格式

前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。

输入输出样例

输入 #1复制

6

输出 #1复制

2 4 6 1 3 5
3 6 2 5 1 4
4 1 5 2 6 3
4

说明/提示

【数据范围】
对于 100%100% 的数据,6≤�≤136≤n≤13。

题目翻译来自NOCOW。

USACO Training Section 1.5

思路:对于八皇后这个题,我们要使用深搜(我认为这里相比于广搜更好)我们防止任意一个点,它的正逆对角线还有行列都是能到达的,这个时候我们需要用几个数组来分别记录是否能到达(行不需要,因为我们就是对行进行搜索),再用一个变量进行计数(由于题目只需输出前三个解,但必须输出总解数),注意一定要回溯,不然会循环下去得不到结果、

代码:

#include
using namespace std;
int m1[30], m2[30], m3[30],ans[30];
int n,mark=0;
void pj(int x,int y,int z) {
	ans[x] = y;
	m1[y] = z;
	m2[x + y] = z;
	m3[x - y + n] = z;
}
void dfs(int step) {
	if (step > n) {
		mark++;
		if (mark <= 3) {
			for (int i = 1; i <= n; i++)
				cout << ans[i] << ' ';
			cout << endl;
		}
		return;
	}
	for (int i = 1; i <= n; i++) {
		if (m1[i] || m2[step + i] || m3[step - i + n])continue;
		pj(step, i, 1);
		dfs(step + 1);
		pj(step, i, 0);
	}
}
int main() {
	cin >> n;
	dfs(1);
	cout << mark;
	return 0;
}

P2392 kkksc03考前临时抱佛脚

题目背景

kkksc03 的大学生活非常的颓废,平时根本不学习。但是,临近期末考试,他必须要开始抱佛脚,以求不挂科。

题目描述

这次期末考试,kkksc03 需要考 44 科。因此要开始刷习题集,每科都有一个习题集,分别有 �1,�2,�3,�4s1​,s2​,s3​,s4​ 道题目,完成每道题目需要一些时间,可能不等(�1,�2,…,��1A1​,A2​,…,As1​​,�1,�2,…,��2B1​,B2​,…,Bs2​​,�1,�2,…,��3C1​,C2​,…,Cs3​​,�1,�2,…,��4D1​,D2​,…,Ds4​​)。

kkksc03 有一个能力,他的左右两个大脑可以同时计算 22 道不同的题目,但是仅限于同一科。因此,kkksc03 必须一科一科的复习。

由于 kkksc03 还急着去处理洛谷的 bug,因此他希望尽快把事情做完,所以他希望知道能够完成复习的最短时间。

输入格式

本题包含 55 行数据:第 11 行,为四个正整数 �1,�2,�3,�4s1​,s2​,s3​,s4​。

第 22 行,为 �1,�2,…,��1A1​,A2​,…,As1​​ 共 �1s1​ 个数,表示第一科习题集每道题目所消耗的时间。

第 33 行,为 �1,�2,…,��2B1​,B2​,…,Bs2​​ 共 �2s2​ 个数。

第 44 行,为 �1,�2,…,��3C1​,C2​,…,Cs3​​ 共 �3s3​ 个数。

第 55 行,为 �1,�2,…,��4D1​,D2​,…,Ds4​​ 共 �4s4​ 个数,意思均同上。

输出格式

输出一行,为复习完毕最短时间。

输入输出样例

输入 #1复制

1 2 1 3		
5
4 3
6
2 4 3

输出 #1复制

20

说明/提示

1≤�1,�2,�3,�4≤201≤s1​,s2​,s3​,s4​≤20。

1≤�1,�2,…,��1,�1,�2,…,��2,�1,�2,…,��3,�1,�2,…,��4≤601≤A1​,A2​,…,As1​​,B1​,B2​,…,Bs2​​,C1​,C2​,…,Cs3​​,D1​,D2​,…,Ds4​​≤60。

思路:

要想得到最少花费的时间,我们要先要得到每个科目所花费的最少时间,针对每个科目进行搜索得到最少耗时,注意要进行回溯(把所有解求一遍来得到最小值)

代码:

#include
using namespace std;
int l, r;
int s[5];
int a[5][25];
int ans = 0;
int minn = 1e9;
void work(int x, int y) {
	if (x > s[y]) {
		minn = min(max(l, r), minn);
		return;
	}
	l += a[y][x];
	work(x + 1, y);
	l -= a[y][x];
	r += a[y][x];
	work(x + 1, y);
	r -= a[y][x];
}
int main() {
	for (int i = 1; i <= 4; i++)cin >> s[i];
	for (int i = 1; i <= 4; i++) {
		for (int j = 1; j <= s[i]; j++) {
			cin >> a[i][j];
		}
		l = 0;
		r = 0;
		work(1, i);
		ans += minn;
		minn = 1e9;
	}
	cout << ans;
	return 0;
}

P2036 [COCI 2008/2009 #2] PERKET

题目描述

Perket 是一种流行的美食。为了做好 Perket,厨师必须谨慎选择食材,以在保持传统风味的同时尽可能获得最全面的味道。你有 �n 种可支配的配料。对于每一种配料,我们知道它们各自的酸度 �s 和苦度 �b。当我们添加配料时,总的酸度为每一种配料的酸度总乘积;总的苦度为每一种配料的苦度的总和。

众所周知,美食应该做到口感适中,所以我们希望选取配料,以使得酸度和苦度的绝对差最小。

另外,我们必须添加至少一种配料,因为没有任何食物以水为配料的。

输入格式

第一行一个整数 �n,表示可供选用的食材种类数。

接下来 �n 行,每行 22 个整数 ��si​ 和 ��bi​,表示第 �i 种食材的酸度和苦度。

输出格式

一行一个整数,表示可能的总酸度和总苦度的最小绝对差。

输入输出样例

输入 #1复制

1
3 10

输出 #1复制

7

输入 #2复制

2
3 8
5 8

输出 #2复制

1

输入 #3复制

4
1 7
2 6
3 8
4 9

输出 #3复制

1

说明/提示

数据规模与约定

对于 100%100% 的数据,有 1≤�≤101≤n≤10,且将所有可用食材全部使用产生的总酸度和总苦度小于 1×1091×109,酸度和苦度不同时为 11 和 00。

说明
  • 本题满分 7070 分。
  • 题目译自 COCI2008-2009 CONTEST #2 PERKET,译者 @mnesia。

附件下载

contest2_tasks.pdf101.88KB

思路:这道题使用深搜很快就能写出来,题目不难就是有点难想到,我们把食材序号,酸度和苦度作为参数进行搜索(前两个样例按照正常思路很容易通过,但是我们需要注意一点:并不是所有食材都要加到一起(黑暗料理),我们的目的是要得到酸苦度的最小差值),进行下一步搜索的时候食材序号一定要+1(当前食材已经搜完了),然后我们可以分成两种情况,一种是在原有基础上算上当前食材的酸度和苦度进行下一步搜索,另一种是不算上当前食材直接跳过进行下一步搜索,我们这样做的目的是为了搜索到更多情况,得到差值最小的答案

代码:

#include
using namespace std;
int a[10], b[10],n,ans=1e9;
void dfs(int x,int y,int z) {
	if (x > n) {
		if (y == 1 && z == 0)return;
		ans = min(ans, abs(y - z));
		return;
	}
	dfs(x + 1, y * a[x], z + b[x]);
	dfs(x + 1, y, z);
}
int main() {
	cin >> n;
	for (int i = 1; i <= n; i++)
		cin >> a[i] >> b[i];
	dfs(1,1,0);
	cout << ans << endl;
	return 0;
}

你可能感兴趣的:(学习,算法)