动态规划练习

搜索( dfs,bfsdfs, bfsdfs,bfs )

P1443 马的遍历

题目描述

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

输入格式

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

输出格式

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

输入输出样例 #1

输入 #1

3 3 1 1

输出 #1

0    3    2    
3    -1   1    
2    1    4

说明/提示

数据规模与约定

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

bfsbfsbfs

一道 bfsbfsbfs 的模板题,先把马最先的位置压入队列,弹出后再压入所有马走一步能到的地方,如果之前已经到达过了这个地方就不重复压入。

最后注意有五格场宽。

#include<bits/stdc++.h>
using namespace std;
int n, m;
int x, y;
int a[402][402];
int xx[8] = { -1,-2,-2,-1,1,2,2,1 };
int yy[8] = { 2,1,-1,-2,-2,-1,1,2 };
struct node {
	int x, y;
};
queue<node>q;
int main() {
	cin >> n >> m >> x >> y;
	memset(a, -1, sizeof(a));
	a[x][y] = 0;
	q.push({ x,y });
	while (!q.empty()) {
		int x1 = q.front().x;
		int y1 = q.front().y;
		q.pop();
		for (int i = 0; i < 8; i++) {
			int x2 = x1 + xx[i];
			int y2 = y1 + yy[i];
			if (x2 > 0 && x2 <= n && y2 > 0 && y2 <= m && a[x2][y2] == -1) {
				a[x2][y2] = a[x1][y1] + 1;
				q.push({ x2,y2 });
			}
		}
	}
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			cout << setw(5) << a[i][j];
		}
		cout << endl;
	}
	return 0;
}

P2895 [USACO08FEB] Meteor Shower S

题目描述

贝茜听说一场特别的流星雨即将到来:这些流星会撞向地球,并摧毁它们所撞击的任何东西。她为自己的安全感到焦虑,发誓要找到一个安全的地方(一个永远不会被流星摧毁的地方)。

如果将牧场放入一个直角坐标系中,贝茜现在的位置是原点,并且,贝茜不能踏上一块被流星砸过的土地。

根据预报,一共有 MMM 颗流星 (1≤M≤50,000)(1\leq M\leq 50,000)(1M50,000) 会坠落在农场上,其中第 iii 颗流星会在时刻 TiT_iTi0≤Ti≤10000 \leq T _ i \leq 10000Ti1000)砸在坐标为 (Xi,Yi)(0≤Xi≤300(X_i,Y_i)(0\leq X_i\leq 300(Xi,Yi)(0Xi3000≤Yi≤300)0\leq Y_i\leq 300)0Yi300) 的格子里。流星的力量会将它所在的格子,以及周围 444 个相邻的格子都化为焦土,当然贝茜也无法再在这些格子上行走。

贝茜在时刻 000 开始行动,她只能在会在横纵坐标 X,Y≥0X,Y\ge 0X,Y0 的区域中,平行于坐标轴行动,每 111 个时刻中,她能移动到相邻的(一般是 444 个)格子中的任意一个,当然目标格子要没有被烧焦才行。如果一个格子在时刻 ttt 被流星撞击或烧焦,那么贝茜只能在 ttt 之前的时刻在这个格子里出现。 贝茜一开始在 (0,0)(0,0)(0,0)

请你计算一下,贝茜最少需要多少时间才能到达一个安全的格子。如果不可能到达输出 −1−11

输入格式

M+1M+1M+1 行,第 111 行输入一个整数 MMM,接下来的 MMM 行每行输入三个整数分别为 Xi,Yi,TiX_i, Y_i, T_iXi,Yi,Ti

输出格式

贝茜到达安全地点所需的最短时间,如果不可能,则为 −1-11

输入输出样例 #1

输入 #1

4
0 0 2
2 1 2
1 1 2
0 3 5

输出 #1

5

也是一道 bfsbfsbfs ,每次移动时检查一下下一步会不会被陨石砸到,除此之外就是一道板子题

#include<bits/stdc++.h>
using namespace std;
int a[302][302];
int b[302][302];
int n;
struct drop {
	int x, y, t;
}m[50005];
struct node {
	int x, y, t;
};
int xb[4] = { -1, 0, 1, 0 };
int yb[4] = { 0, -1, 0, 1 };
int t1 = 0;
queue<node>q;
int main() {
	cin >> n;
	memset(a, -1, sizeof(a));
	for (int i = 0; i < n; i++) {
		cin >> m[i].x >> m[i].y >> m[i].t;
	}
	for (int i = 0; i < n; i++) {
		int xx = m[i].x;
		int yy = m[i].y;
		int tt = m[i].t;
		if (a[xx][yy] == -1)a[xx][yy] = tt;
		else a[xx][yy] = min(a[xx][yy], tt);
		for (int j = 0; j < 4; j++) {
			int xxx = xx + xb[j];
			int yyy = yy + yb[j];
			if (xxx >= 0 && yyy >= 0){
				if (a[xxx][yyy] == -1)a[xxx][yyy] = tt;
				else a[xxx][yyy] = min(a[xxx][yyy], tt);
			}
		}
	}
	q.push({ 0, 0 ,0 });
	b[0][0] = 1;
	while (!q.empty()) {
		int x2 = q.front().x;
		int y2 = q.front().y;
		int t2 = q.front().t;
		q.pop();
		if (a[x2][y2] == -1) {
			cout << t2;
			return 0;
		}
		for (int i = 0; i < 4; i++) {
			int x3 = x2 + xb[i];
			int y3 = y2 + yb[i];
			int t3 = t2 + 1;
			if (x3 >= 0 && y3 >= 0 && (t3 < a[x3][y3] || a[x3][y3] == -1) && !b[x3][y3]) {
				q.push({ x3,y3,t3 });
				b[x3][y3] = t3;
			}
		}
	}
	cout << -1;
	return 0;
}

P1036 [NOIP 2002 普及组] 选数

题目描述

已知 nnn 个整数 x1,x2,⋯ ,xnx_1,x_2,\cdots,x_nx1,x2,,xn,以及 111 个整数 kkkk<nk<nk<n)。从 nnn 个整数中任选 kkk 个整数相加,可分别得到一系列的和。例如当 n=4n=4n=4k=3k=3k=3444 个整数分别为 3,7,12,193,7,12,193,7,12,19 时,可得全部的组合与它们的和为:

3+7+12=223+7+12=223+7+12=22

3+7+19=293+7+19=293+7+19=29

7+12+19=387+12+19=387+12+19=38

3+12+19=343+12+19=343+12+19=34

现在,要求你计算出和为素数共有多少种。

例如上例,只有一种的和为素数:3+7+19=293+7+19=293+7+19=29

输入格式

第一行两个空格隔开的整数 n,kn,kn,k1≤n≤201 \le n \le 201n20k<nk<nk<n)。

第二行 nnn 个整数,分别为 x1,x2,⋯ ,xnx_1,x_2,\cdots,x_nx1,x2,,xn1≤xi≤5×1061 \le x_i \le 5\times 10^61xi5×106)。

输出格式

输出一个整数,表示种类数。

输入输出样例 #1

输入 #1

4 3
3 7 12 19

输出 #1

1

说明/提示

【题目来源】

NOIP 2002 普及组第二题

dfsdfsdfs 选够数后检查和是否是素数

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e8 + 10;
int ans = 0;
int b[22];
int a[22];
int n, k;
int sum = 0;
stack<int>s;
bool check(int x) {
	for (int i = 2; i * i < x; i++) {
		if (x % i == 0)return false;
	}
	return true;
}
void dfs(int num, int x) {
	if (num == k) {
		if (check(sum))ans++;
		return;
	}

	for (int i = x + 1; i <= n; i++) {
		b[i] = 1;
		sum += a[i];
		dfs(num + 1, i);
		b[i] = 0;
		sum -= a[i];
	}
}
int main() {
	cin >> n >> k;
	for (int i = 1; i <= n; i++)cin >> a[i];
	dfs(0, 0);
	cout << ans;
	return 0;
}

P2036 [COCI 2008/2009 #2] PERKET

题目描述

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

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

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

输入格式

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

接下来 nnn 行,每行 222 个整数 sis_isibib_ibi,表示第 iii 种食材的酸度和苦度。

输出格式

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

输入输出样例 #1

输入 #1

1
3 10

输出 #1

7

输入输出样例 #2

输入 #2

2
3 8
5 8

输出 #2

1

输入输出样例 #3

输入 #3

4
1 7
2 6
3 8
4 9

输出 #3

1

说明/提示

数据规模与约定

对于 100%100\%100% 的数据,有 1≤n≤101 \leq n \leq 101n10,且将所有可用食材全部使用产生的总酸度和总苦度小于 1×1091 \times 10^91×109,酸度和苦度不同时为 111000

dfsdfsdfs 数很大我开的 longlonglong longlonglong

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n;
struct food {
	ll si, bi;
}f[10];
ll abs1(ll a) {
	return a > 0 ? a : -a;
}
ll min(ll a, ll b) {
	return a < b ? a : b;
}
ll s = 1;
ll b = 0;
ll minn;
void dfs(int x){
	for (int i = x + 1; i <= n; i++) {
		s *= f[i].si;
		b += f[i].bi;
		minn = min(abs(s - b), minn);
		dfs(i);
		s /= f[i].si;
		b -= f[i].bi;
	}
}
int main() {
	cin >> n;
	for (int i = 1; i <= n; i++)cin >> f[i].si >> f[i].bi;
	minn = abs1(f[1].si - f[1].bi);
	dfs(0);
	cout << minn;
	return 0;
}

P1605 迷宫

题目描述

给定一个 N×MN \times MN×M 方格的迷宫,迷宫里有 TTT 处障碍,障碍处不可通过。

在迷宫中移动有上下左右四种方式,每次只能移动一个方格。数据保证起点上没有障碍。

给定起点坐标和终点坐标,每个方格最多经过一次,问有多少种从起点坐标到终点坐标的方案。

输入格式

第一行为三个正整数 N,M,TN,M,TN,M,T,分别表示迷宫的长宽和障碍总数。

第二行为四个正整数 SX,SY,FX,FYSX,SY,FX,FYSX,SY,FX,FYSX,SYSX,SYSX,SY 代表起点坐标,FX,FYFX,FYFX,FY 代表终点坐标。

接下来 TTT 行,每行两个正整数,表示障碍点的坐标。

输出格式

输出从起点坐标到终点坐标的方案总数。

输入输出样例 #1

输入 #1

2 2 1
1 1 2 2
1 2

输出 #1

1

说明/提示

对于 100%100\%100% 的数据,1≤N,M≤51 \le N,M \le 51N,M51≤T≤101 \le T \le 101T101≤SX,FX≤n1 \le SX,FX \le n1SX,FXn1≤SY,FY≤m1 \le SY,FY \le m1SY,FYm

dfsdfsdfs

#include<bits/stdc++.h>
using namespace std;
int n, m, t;
int sx, sy, fx, fy;
int ans;
int xx[4] = { -1,0,1,0 };
int yy[4] = { 0,-1,0,1 };
int a[7][7];
int b[7][7];
void dfs(int x1, int y1) {
	if (x1 == fx && y1 == fy) {
		ans++;
		return;
	}
	for (int i = 0; i < 4; i++) {
		int x2 = x1 + xx[i];
		int y2 = y1 + yy[i];
		if (x2 >= 1 && x2 <= n && y2 >= 1 && y2 <= m && !a[x2][y2] && !b[x2][y2]) {
			b[x2][y2] = 1;
			dfs(x2, y2);
			b[x2][y2] = 0;
		}
	}
}
int main() {
	cin >> n >> m >> t;
	cin >> sx >> sy >> fx >> fy;
	for (int i = 0; i < t; i++) {
		int p, q;
		cin >> p >> q;
		a[p][q] = 1;
	}
	b[sx][sy] = 1;
	dfs(sx, sy);
	cout << ans;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值