这两天大部分时间都在完成洛谷团队上的算法题,对于搜索这一类型的题目不再像之前一样没有头绪,能够有一些基本的想法和框架,能较快想到使用哪一种搜索(有些题目深搜广搜感觉都能做,但是目前还没有达到那个水平),但还是会经常卡住,有些地方想到了却不知道怎么用代码去实现,可能是不够熟练的原因导致思路时常打不开,怎样去更好的使用搜索,或者使用的更加灵活还是需要去多加练习。
今天进行了寒假预备役的第一次测试,敲了三道题,但是只有第一道题样例全过,自己一时半会也没找出问题来,之后抽空把题都补一下,看看是哪些细节没有注意到(学了几天的搜索发现前面的一些东西也忘记了很多,还是要多复习多巩固),接下来这一周里打算先继续写完洛谷团队里的题,然后周末把这周学的东西和写了的题目全部再看一看,整理一下思路。
目录
P2404 自然数的拆分问题
P1219 [USACO1.5] 八皇后 Checker Challenge(该问题是非常经典的搜索问题)
P2392 kkksc03考前临时抱佛脚
P2036 [COCI 2008/2009 #2] PERKET
任何一个大于 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;
}
一个如下的 6×66×6 的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。
上面的布局可以用序列 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;
}
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;
}
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。
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;
}