2022/1/9总结

刚回家被挺多事情缠上了,比如亲友聚会,但是也尽量补足时间去学习。

昨天回来和今天总共差不多全在干搜索的题目,不多说,直接上题目和代码。


题目背景

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

题目描述

这次期末考试,kkksc03 需要考 44 科。因此要开始刷习题集,每科都有一个习题集,分别有 s_1,s_2,s_3,s_4s1​,s2​,s3​,s4​ 道题目,完成每道题目需要一些时间,可能不等(A_1,A_2,\ldots,A_{s_1}A1​,A2​,…,As1​​,B_1,B_2,\ldots,B_{s_2}B1​,B2​,…,Bs2​​,C_1,C_2,\ldots,C_{s_3}C1​,C2​,…,Cs3​​,D_1,D_2,\ldots,D_{s_4}D1​,D2​,…,Ds4​​)。

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

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

输入格式

本题包含 55 行数据:第 11 行,为四个正整数 s_1,s_2,s_3,s_4s1​,s2​,s3​,s4​。

第 22 行,为 A_1,A_2,\ldots,A_{s_1}A1​,A2​,…,As1​​ 共 s_1s1​ 个数,表示第一科习题集每道题目所消耗的时间。

第 33 行,为 B_1,B_2,\ldots,B_{s_2}B1​,B2​,…,Bs2​​ 共 s_2s2​ 个数。

第 44 行,为 C_1,C_2,\ldots,C_{s_3}C1​,C2​,…,Cs3​​ 共 s_3s3​ 个数。

第 55 行,为 D_1,D_2,\ldots,D_{s_4}D1​,D2​,…,Ds4​​ 共 s_4s4​ 个数,意思均同上。

输出格式

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

输入输出样例

输入 #1复制

1 2 1 3		
5
4 3
6
2 4 3

输出 #1复制

20

说明/提示

1\leq s_1,s_2,s_3,s_4\leq 201≤s1​,s2​,s3​,s4​≤20。

1\leq A_1,A_2,\ldots,A_{s_1},B_1,B_2,\ldots,B_{s_2},C_1,C_2,\ldots,C_{s_3},D_1,D_2,\ldots,D_{s_4}\leq601≤A1​,A2​,…,As1​​,B1​,B2​,…,Bs2​​,C1​,C2​,…,Cs3​​,D1​,D2​,…,Ds4​​≤60。

怎么说呢,刚开始这道题我想用模拟去解,可是样例能过测试点全部WA,后面我就在想是不是贪心问题,然后就想到了背包。因为是一科一科复习,所以可以分成4个任务去完成。因为是双线程(左右脑),那么最佳的处理时间方案应该是每半脑应该都负责差不多一半的问题处理时间,即两半脑处理时间差得最小,那么我们可以理解为:背包容量就是该任务所有问题处理的总时间的一半,即total / 2,每个问题的价值等同于它的空间,只要用背包去找到一个半脑最靠近处理总时间的一半的答案,那么该任务消耗的时间就是另一半脑所消耗的时间,即 总时间-背包得出的半脑时间,用该方法去处理4个任务,完成时间的计算,最后输出即可。

下面贴上代码:

#include <bits/stdc++.h>

using namespace std;

void FTime(int *Array, int sn, int *time);

int main()
{
	int s1, s2, s3, s4;
	cin >> s1 >> s2 >> s3 >> s4;
	int a[s1], b[s2], c[s3], d[s4];
	int time = 0;
	for(int i = 0;i < s1;i ++)
	{
		cin >> a[i];
	}
	for(int i = 0;i < s2;i ++)
	{
		cin >> b[i];
	}
	for(int i = 0;i < s3;i ++)
	{
		cin >> c[i];
	}
	for(int i = 0;i < s4;i ++)
	{
		cin >> d[i];
	}
	FTime(a, s1, &time);
	FTime(b, s2, &time);
	FTime(c, s3, &time);
	FTime(d, s4, &time);
	cout << time;
	return 0;
}

void FTime(int *Array, int sn, int *time)
{
	int total = 0;
	for(int i = 0;i < sn;i ++)
	{
		total += *(Array + i);
	}
	int dp[total / 2 + 1] = {0};
	for(int j = 0;j < sn;j ++)
	{
		for(int i = total / 2;i >= *(Array + j);i --)
		{
			dp[i] = max(dp[i], dp[i - *(Array + j)] + *(Array + j));
		}	
	}
	*time += total - dp[total / 2];
}

题目描述

有一个 n \times mn×m 的棋盘,在某个点 (x, y)(x,y) 上有一个马,要求你计算出马到达棋盘上任意一个点最少要走几步。

输入格式

输入只有一行四个整数,分别为 n, m, x, yn,m,x,y。

输出格式

一个 n \times mn×m 的矩阵,代表马到达某个点最少要走几步(左对齐,宽 55 格,不能到达则输出 -1−1)。

输入输出样例

输入 #1复制

3 3 1 1

输出 #1复制

0    3    2    
3    -1   1    
2    1    4    

说明/提示

数据规模与约定

对于全部的测试点,保证 1 \leq x \leq n \leq 4001≤x≤n≤400,1 \leq y \leq m \leq 4001≤y≤m≤400。

这道题第一眼,直觉告诉我要用bfs,搭配新学到的组合queue+bfs,解起来会很方便。其本质就是记录起点到所有地方可能的步数,遍历方法有点像迷宫,都是记录是否到达过,再给每个位置一个答案。不多说,上AC代码。

#include <bits/stdc++.h>

using namespace std;

int fx[8] = {1, 2, 2, 1, -1, -2, -2, -1};
int fy[8] = {2, 1, -1, -2, -2, -1, 1, 2};

int main()
{
	int n, m, sx, sy;
	cin >> n >> m >> sx >> sy;
	int vis[n + 5][m + 5] = {0}; //标记是否访问
	int steps[402][402];
	memset(steps, -1, 402 * 402); //默认全部不可到达
	steps[sx][sy] = 0; //因为是出发点,所以步数为0
	vis[sx][sy] = 1; //开始的x, y均被访问过了
	queue <int> qx, qy; //代表x和y的队列,用于队列化处理数据
	qx.push(sx);
	qy.push(sy);
	while(!(qx.empty()) && !(qy.empty())) //队列不为空时就一直执行
	{
		int nx, ny;
		for(int i = 0;i < 8;i ++)
		{
//			cout << qx.front() << " " << qy.front() << "->";
			nx = qx.front() + fx[i];
			ny = qy.front() + fy[i];
//			cout << nx << " " << ny << endl;
			if(nx >= 1 && nx <= n && ny >= 1 && ny <= m && vis[nx][ny] != 1) //判断是否越界且是否未访问
			{
				steps[nx][ny] = steps[qx.front()][qy.front()] + 1; //相当于就是上一步的步数加1
				qx.push(nx); //加入队列,下一个循环处理
				qy.push(ny);
				vis[nx][ny] = 1;
			}
		}
		qx.pop(); //遍历过的,给去除
		qy.pop();
	}	
	for(int i = 1;i <= n;i ++)
	{
		for(int j = 1;j <= m;j ++)
		{
			printf("%-5d", steps[i][j]); //输出
		}
		cout << endl;
	}
	return 0;
}

题目描述

呵呵,有一天我做了一个梦,梦见了一种很奇怪的电梯。大楼的每一层楼都可以停电梯,而且第ii层楼(1 \le i \le N)(1≤i≤N)上有一个数字K_i(0 \le K_i \le N)Ki​(0≤Ki​≤N)。电梯只有四个按钮:开,关,上,下。上下的层数等于当前楼层上的那个数字。当然,如果不能满足要求,相应的按钮就会失灵。例如:3, 3 ,1 ,2 ,53,3,1,2,5代表了K_i(K_1=3,K_2=3,…)Ki​(K1​=3,K2​=3,…),从11楼开始。在11楼,按“上”可以到44楼,按“下”是不起作用的,因为没有-2−2楼。那么,从AA楼到BB楼至少要按几次按钮呢?

输入格式

共二行。

第一行为33个用空格隔开的正整数,表示N,A,B(1≤N≤200, 1≤A,B≤N)N,A,B(1≤N≤200,1≤A,B≤N)。

第二行为NN个用空格隔开的非负整数,表示K_iKi​。

输出格式

一行,即最少按键次数,若无法到达,则输出-1−1。

输入输出样例

输入 #1复制

5 1 5
3 3 1 2 5

输出 #1复制

3

一个更“低维”的迷宫,解法同样是queue+bfs,不需要回溯。比较简单,直接上代码。

#include <bits/stdc++.h>

using namespace std;

int main()
{
	int n, a, b;
	int vis[300] = {0};
	cin >> n >> a >> b;
	int fn[n + 1];
	for(int i = 1;i <= n;i ++)
	{
		scanf("%d", &fn[i]);
	}
	queue <int> qfn;
	qfn.push(a);
	vis[a] = 1;
	int steps[300];
	memset(steps, -1, sizeof(steps));
	steps[a] = 0;
	while(!qfn.empty())
	{
		int df1, df2;
		df1 = qfn.front() + fn[qfn.front()];
		df2 = qfn.front() - fn[qfn.front()];
		if(df1 <= n && vis[df1] != 1)
		{
//			cout << df1 << endl;
//			cout << "From " << qfn.front() << " goto " << df1 << endl;
			steps[df1] = steps[qfn.front()] + 1;
			qfn.push(df1);
			vis[df1] = 1;
		}
		if(df2 >= 1 && vis[df2] != 1)
		{
//			cout << "From " << qfn.front() << " goto " << df2 << endl;
			steps[df2] = steps[qfn.front()] + 1;
			qfn.push(df2);
			vis[df2] = 1;
		}		
		qfn.pop();
	}
	cout << steps[b];
	return 0;
}

题目描述

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

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

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

输入格式

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

接下来 nn 行,每行 22 个整数 s_isi​ 和 b_ibi​,表示第 ii 种食材的酸度和苦度。

输出格式

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

输入输出样例

输入 #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 \leq n \leq 101≤n≤10,且将所有可用食材全部使用产生的总酸度和总苦度小于 1 \times 10^91×109,酸度和苦度不同时为 11 和 00。

这道题,因为数据范围比较小,所以可以直接暴力dfs找最小,就是min(res, abs(sox - bdx)),即酸度乘积和苦度和的最小绝对差。上代码:

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

struct foods {
	ll so; //酸度
	ll bd; //苦度
};

void dfs(ll *res, ll sox, ll bdx, struct foods *fd, int num, int n);

int main()
{
	ios::sync_with_stdio(false);
	int n;
	cin >> n;
	struct foods fd[n];
	for(int i = 0;i < n;i ++)
	{
		cin >> fd[i].so >> fd[i].bd;
	}
	ll res = 1e9 + 7; //差最小值
	dfs(&res, 1, 0, fd, 0, n);
	cout << res;
	return 0;
}

void dfs(ll *res, ll sox, ll bdx, struct foods *fd, int num, int n)
{
	if(num >= n)
	{
		if(bdx == 0) return; //必须要选一种食材,没选的不算
		*res = min(*res, abs(sox - bdx));
		return;
	}
	dfs(res, sox, bdx, fd, num + 1, n); //假设不选
	dfs(res, sox * (fd + num)->so, bdx + (fd + num)->bd, fd, num + 1, n); //假设选
}

题目背景

给定一个N*M方格的迷宫,迷宫里有T处障碍,障碍处不可通过。给定起点坐标和终点坐标,问: 每个方格最多经过1次,有多少种从起点坐标到终点坐标的方案。在迷宫中移动有上下左右四种方式,每次只能移动一个方格。数据保证起点上没有障碍。

题目描述

输入格式

第一行N、M和T,N为行,M为列,T为障碍总数。第二行起点坐标SX,SY,终点坐标FX,FY。接下来T行,每行为障碍点的坐标。

输出格式

给定起点坐标和终点坐标,问每个方格最多经过1次,从起点坐标到终点坐标的方案总数。

输入输出样例

输入 #1复制

2 2 1
1 1 2 2
1 2

输出 #1复制

1

说明/提示

【数据规模】

1≤N,M≤5

怎么说呢,这道题和我们之前的范例迷宫有点像,虽然说不必要记录路径只需要输出路径数量,但是都要用到dfs和回溯。即在“路径打通”后“返回上一个位置”和“寻找下条不同路径”或者“路径不通”后“返回上一个位置”和“寻找可能路径”,要做到这点需要回溯。上AC代码:

#include <bits/stdc++.h>

using namespace std;

int dx[4] = {0, 0, -1, 1};
int dy[4] = {1, -1, 0, 0}; 

int vis[6][6] = {0};
int bar[6][6] = {0};
int ways = 0;
int sx, sy, fx, fy;

void dfs(int xx, int yy, int N, int M);

int main()
{
	ios::sync_with_stdio(false);
	int N, M, T;
	cin >> N >> M >> T;
	cin >> sx >> sy >> fx >> fy;
	for(int i = 0;i < T;i ++)
	{
		int x, y;
		cin >> x >> y;
		bar[x][y] = 1;
	}
	vis[sx][sy] = 1;
	dfs(sx, sy, N, M);
	cout << ways;
	return 0;
}

void dfs(int xx, int yy, int N, int M)
{
	if(xx == fx && yy == fy)
	{
		vis[xx][yy] = 0;
		ways ++;
		return;
	}
	int nx, ny;
	for(int i = 0;i < 4;i ++)
	{
		nx = xx + dx[i];
		ny = yy + dy[i];
		if(nx >= 1 && nx <= N && ny >= 1 && ny <= M && vis[nx][ny] != 1 && bar[nx][ny] != 1)
		{
			vis[nx][ny] = 1;
			dfs(nx, ny, N, M);
			vis[nx][ny] = 0;
		}
	}
	return;
}

题目描述

任何一个大于1的自然数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

说明/提示

用回溯做。。。。

n\le 8n≤8

题目提示用回溯做,那就用回溯呗。既然要回溯,那肯定得记下来“路径”,在本题中,“路径”即上一个减去的数字,只要我们找到“通路”后“返回”并“寻找下一通路”即可,因为这题要求字典序小的结果排在前面,那么只要从小的减数开始减,保证下一个减数大于等于上一个减数,就可以做到字典序排列答案了。放上AC代码:

#include <bits/stdc++.h>

void dfs(int n_, int n, int *record, int step);

using namespace std;

int main()
{
	int n;
	cin >> n;
	int record[9] = {0}; //用于记录减数
	dfs(n, n, record + 1, 1);
	return 0;
}

void dfs(int n_, int n, int *record, int step)
{
//  从比上个减数不小的数遍历到不大于n的数用来相减
	for(int i = *(record - 1) == 0 ? 1 : *(record - 1);i < n_;i ++) 
	{
		int sub = i;
		if(sub <= n)
		{
//			cout << "Try to sub " << sub << endl;
			if(n - sub == 0) //减去到0了
			{
				*record = sub; //记录减数
//				cout << *record << endl;
				for(int i = -step + 1;i < 0;i ++) //输出部分
				{
					cout << *(record + i) << "+";
				}
				cout << *(record) << endl;
			}
			else
			{
				*record = sub; //记录减数
//				cout << *record << endl;
				dfs(n_, n - sub, record + 1, step + 1); //还没减到0,继续减
			}
		}
	}
}

接下来,听说链表的题目开了,赶紧去做做。然后买了本《大话数据结构》打算看看,这么多时间才刷了这么些题目卡了好久,深知自己的不足,还需要提升。努力在这个星期内把指标超前达成吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ISansXI

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值