(Week 11)综合复习(C++,图论,动态规划,搜索)

news2024/12/22 19:18:17

目录

  • 汤姆斯的天堂梦(C++,Dijkstra)
    • 题目描述
    • 输入格式
    • 输出格式
    • 样例 #1
      • 样例输入 #1
      • 样例输出 #1
    • 提示
    • 解题思路:
  • [蓝桥杯 2022 国 A] 环境治理(C++,Floyd)
    • 题目描述
    • 输入格式
    • 输出格式
    • 样例 #1
      • 样例输入 #1
      • 样例输出 #1
    • 提示
    • 解题思路:
  • 跑步(C++,01背包方案数,DP)
    • 题目描述
    • 输入格式
    • 输出格式
    • 样例 #1
      • 样例输入 #1
      • 样例输出 #1
    • 提示
        • 数据规模与约定
    • 解题思路:
  • 遗址(C++,搜索)
    • 题目描述
    • 输入格式
    • 输出格式
    • 样例 #1
      • 样例输入 #1
      • 样例输出 #1
    • 提示
    • 解题思路:
  • [蓝桥杯 2021 省 AB] 砝码称重(C++,01背包可行性)
    • 题目描述
    • 输入格式
    • 输出格式
    • 样例 #1
      • 样例输入 #1
      • 样例输出 #1
    • 提示
    • 解题思路:

汤姆斯的天堂梦(C++,Dijkstra)

题目描述

汤姆斯生活在一个等级为 0 0 0 的星球上。那里的环境极其恶劣,每天 12 12 12 小时的工作和成堆的垃圾让人忍无可忍。他向往着等级为 N N N 的星球上天堂般的生活。

有一些航班将人从低等级的星球送上高一级的星球,有时需要向驾驶员支付一定金额的费用,有时却又可以得到一定的金钱。

汤姆斯预先知道了从 0 0 0 等级星球去 N N N 等级星球所有的航线和需要支付(或者可以得到)的金钱,他想寻找一条价格最低(甚至获得金钱最多)的航线。

输入格式

第一行一个正整数 N N N N ≤ 100 N \le 100 N100),接下来的数据可分为 N N N 个段落,每段的第一行一个整数 K i K_i Ki K i ≤ 100 K_i \le 100 Ki100),表示等级为 i i i 的星球有 K i K_i Ki 个。

接下来的 K i K_i Ki 行中第 j j j 行依次表示与等级为 i i i,编号为 j j j 的星球相连的等级为 i − 1 i - 1 i1 的星球的编号和此航线需要的费用(正数表示支出,负数表示收益,费用的绝对值不超过 1000 1000 1000)。

每行以 0 0 0 结束,每行的航线数 ≤ 100 \le 100 100

输出格式

输出所需(或所得)费用。正数表示支出,负数表示收益。

样例 #1

样例输入 #1

3
2
1 15 0
1 5 0
3
1 -5 2 10 0
1 3 0
2 40 0
2
1 1 2 5 3 -5 0
2 -19 3 -20 0

样例输出 #1

-1

提示

对于 100 % 100 \% 100% 的数据, 1 ≤ N ≤ 100 1 \le N \le 100 1N100 1 ≤ K i ≤ 100 1 \le K_i \le 100 1Ki100

样例解释:

解题思路:

单源最短路径,可以想到使用Dijkstra,但鉴于输入数据的特殊性,还需要做一些处理

首先,注意到本题每个节点是由等级和编号唯一确定的

但是我们仍可将其抽象为唯一的编号,如样例中的点可以标号为 1 1 1~ 8 8 8

然后,注意到本题中的边权可能为负数,要知道Dijkstra中的边权不能为负

所有边权整体加上一个偏移量即可

那么存图的问题解决了,接下来就是套用Dijkstra算法了

AC代码如下

#include <iostream>
#include <vector>
#include <queue>
#include <memory.h>
using namespace std;
const int max_n = 100;
const int max_k = 100;
const int max_m = 100;
const int NaN = 0x3F3F3F3F;

class greater_queue {
public:
	bool operator()(pair<int, int>p_1, pair<int, int>p_2) {
		return p_1.first > p_2.first;
	}
};

priority_queue<pair<int, int>, vector<pair<int, int>>, greater_queue>p_q;
struct edge { int v, p, next; }edges[max_n * max_k * max_m];//链式前向星
int head[max_n * max_k + 2] = { -1 };
int tot = -1;
int min_dist[max_n * max_k + 2] = { NaN };
bool book[max_n * max_k + 2] = { false };//最短路径集

void add_edge(int u, int v, int p) {//存图
	edges[++tot] = { v,p,head[u] }; head[u] = tot;
}

void dijkstra() {
	min_dist[1] = 0;
	p_q.push(pair<int, int>(0, 1));//初始化

	while (!p_q.empty()) {
		int node = p_q.top().second;
		int dist = p_q.top().first;
		p_q.pop();

		if (book[node]) continue;//已加入最短路径集
		book[node] = true;//未加入最短路径集

		for (int i = head[node]; i != -1; i = edges[i].next) {//尝试更新最短路径
			int v = edges[i].v;
			if (min_dist[v] > edges[i].p + dist) {
				min_dist[v] = edges[i].p + dist;
				p_q.push(pair<int, int>(min_dist[v], v));
			}
		}
	}
}

int main() {
	memset(head + 1, -1, sizeof(int) * (max_n * max_k + 1));
	memset(min_dist + 1, 0x3F, sizeof(int) * (max_n * max_k + 1));//初始化
	int n, k, u, p, cur_tot = 1, temp_tot = 0;
	cin >> n;
	for (int i = 1; i <= n; i++) {//存图
		cin >> k;
		for (int j = 1; j <= k; j++) {
			++cur_tot;//节点分配
			while (true) {
				cin >> u;
				if (u == 0) break;//行尾
				cin >> p;
				add_edge(temp_tot + u, cur_tot, p + 1000);//边权偏移
			}
		}
		temp_tot = cur_tot - k;//寻访i-1级星球
	}

	dijkstra();

	int ans = NaN;
	for (int i = temp_tot + 1; i <= cur_tot; i++) {//寻找最小值
		ans = min(ans, min_dist[i]);
	}
	cout << ans - 1000 * n << endl;//平衡偏移、输出
	return 0;
}

[蓝桥杯 2022 国 A] 环境治理(C++,Floyd)

题目描述

LQ 国拥有 n n n 个城市,从 0 0 0 n − 1 n - 1 n1 编号,这 n n n 个城市两两之间都有且仅有一条双向道路连接,这意味着任意两个城市之间都是可达的。每条道路都有一个属性 D D D,表示这条道路的灰尘度。当从一个城市 A 前往另一个城市 B 时,可能存在多条路线,每条路线的灰尘度定义为这条路线所经过的所有道路的灰尘度之和,LQ 国的人都很讨厌灰尘,所以他们总会优先选择灰尘度最小的路线。

LQ 国很看重居民的出行环境,他们用一个指标 P P P 来衡量 LQ 国的出行环境, P P P 定义为:

P = ∑ i = 0 n − 1 ∑ j = 0 n − 1 d ( i , j ) P=\sum \limits_{i=0}^{n-1} \sum \limits_{j=0}^{n-1} d(i,j) P=i=0n1j=0n1d(i,j)

其中 d ( i , j ) d(i,j) d(i,j) 表示城市 i i i 到城市 j j j 之间灰尘度最小的路线对应的灰尘度的值。

为了改善出行环境,每个城市都要有所作为,当某个城市进行道路改善时,会将与这个城市直接相连的所有道路的灰尘度都减少 1 1 1,但每条道路都有一个灰尘度的下限值 L L L,当灰尘度达到道路的下限值时,无论再怎么改善,道路的灰尘度也不会再减小了。

具体的计划是这样的:

  • 1 1 1 天, 0 0 0 号城市对与其直接相连的道路环境进行改善;
  • 2 2 2 天, 1 1 1 号城市对与其直接相连的道路环境进行改善;

……

  • n n n 天, n − 1 n - 1 n1 号城市对与其直接相连的道路环境进行改善;
  • n + 1 n + 1 n+1 天, 0 0 0 号城市对与其直接相连的道路环境进行改善;
  • n + 2 n + 2 n+2 天, 1 1 1 号城市对与其直接相连的道路环境进行改善;

……

LQ 国想要使得 P P P 指标满足 P ≤ Q P \leq Q PQ。请问最少要经过多少天之后, P P P 指标可以满足 P ≤ Q P \leq Q PQ。如果在初始时就已经满足条件,则输出 0 0 0;如果永远不可能满足,则输出 − 1 -1 1

输入格式

输入的第一行包含两个整数 n , Q n, Q n,Q,用一个空格分隔,分别表示城市个数和期望达到的 P P P 指标。

接下来 n n n 行,每行包含 n n n 个整数,相邻两个整数之间用一个空格分隔,其中第 i i i 行第 j j j 列的值 D i , j ( D i , j = D j , i , D i , i = 0 ) D_{i,j} (D_{i,j}=D_{j,i},D_{i,i} = 0) Di,j(Di,j=Dj,i,Di,i=0) 表示城市 i i i 与城市 j j j 之间直接相连的那条道路的灰尘度。

接下来 n n n 行,每行包含 n n n 个整数,相邻两个整数之间用一个空格分隔,其中第 i i i 行第 j j j 列的值 L i , j ( L i , j = L j , i , L i , i = 0 ) L_{i,j} (L_{i,j} = L_{j,i}, L_{i,i} = 0) Li,j(Li,j=Lj,i,Li,i=0) 表示城市 i i i 与城市 j j j 之间直接相连的那条道路的灰尘度的下限值。

输出格式

输出一行包含一个整数表示答案。

样例 #1

样例输入 #1

3 10
0 2 4
2 0 1
4 1 0
0 2 2
2 0 0
2 0 0

样例输出 #1

2

提示

【样例说明】

初始时的图如下所示,每条边上的数字表示这条道路的灰尘度:

此时每对顶点之间的灰尘度最小的路线对应的灰尘度为:

  • d ( 0 , 0 ) = 0 , d ( 0 , 1 ) = 2 , d ( 0 , 2 ) = 3 d(0, 0) = 0, d(0, 1) = 2, d(0, 2) = 3 d(0,0)=0,d(0,1)=2,d(0,2)=3
  • d ( 1 , 0 ) = 2 , d ( 1 , 1 ) = 0 , d ( 1 , 2 ) = 1 d(1, 0) = 2, d(1, 1) = 0, d(1, 2) = 1 d(1,0)=2,d(1,1)=0,d(1,2)=1
  • d ( 2 , 0 ) = 3 , d ( 2 , 1 ) = 1 , d ( 2 , 2 ) = 0 d(2, 0) = 3, d(2, 1) = 1, d(2, 2) = 0 d(2,0)=3,d(2,1)=1,d(2,2)=0

初始时的 P P P 指标为 ( 2 + 3 + 1 ) × 2 = 12 (2 + 3 + 1) \times 2 = 12 (2+3+1)×2=12,不满足 P ≤ Q = 10 P \leq Q = 10 PQ=10;

第一天, 0 0 0 号城市进行道路改善,改善后的图示如下:

注意到边 ( 0 , 2 ) (0, 2) (0,2) 的值减小了 1 1 1,但 ( 0 , 1 ) (0, 1) (0,1) 并没有减小,因为 L 0 , 1 = 2 L_{0,1} = 2 L0,1=2 ,所以 ( 0 , 1 ) (0, 1) (0,1) 的值不可以再减小了。此时每对顶点之间的灰尘度最小的路线对应的灰尘度为:

  • d ( 0 , 0 ) = 0 , d ( 0 , 1 ) = 2 , d ( 0 , 2 ) = 3 d(0, 0) = 0, d(0, 1) = 2, d(0, 2) = 3 d(0,0)=0,d(0,1)=2,d(0,2)=3
  • d ( 1 , 0 ) = 2 , d ( 1 , 1 ) = 0 , d ( 1 , 2 ) = 1 d(1, 0) = 2, d(1, 1) = 0, d(1, 2) = 1 d(1,0)=2,d(1,1)=0,d(1,2)=1
  • d ( 2 , 0 ) = 3 , d ( 2 , 1 ) = 1 , d ( 2 , 2 ) = 0 d(2, 0) = 3, d(2, 1) = 1, d(2, 2) = 0 d(2,0)=3,d(2,1)=1,d(2,2)=0

此时 P P P 仍为 12 12 12

第二天,1 号城市进行道路改善,改善后的图示如下:

此时每对顶点之间的灰尘度最小的路线对应的灰尘度为:

  • d ( 0 , 0 ) = 0 , d ( 0 , 1 ) = 2 , d ( 0 , 2 ) = 2 d(0, 0) = 0, d(0, 1) = 2, d(0, 2) = 2 d(0,0)=0,d(0,1)=2,d(0,2)=2
  • d ( 1 , 0 ) = 2 , d ( 1 , 1 ) = 0 , d ( 1 , 2 ) = 0 d(1, 0) = 2, d(1, 1) = 0, d(1, 2) = 0 d(1,0)=2,d(1,1)=0,d(1,2)=0
  • d ( 2 , 0 ) = 2 , d ( 2 , 1 ) = 0 , d ( 2 , 2 ) = 0 d(2, 0) = 2, d(2, 1) = 0, d(2, 2) = 0 d(2,0)=2,d(2,1)=0,d(2,2)=0

此时的 P P P 指标为 ( 2 + 2 ) × 2 = 8 < Q (2 + 2) \times 2 = 8 < Q (2+2)×2=8<Q,此时已经满足条件。

所以答案是 2 2 2

【评测用例规模与约定】

  • 对于 30 % 30\% 30% 的评测用例, 1 ≤ n ≤ 10 1 \leq n \leq 10 1n10 0 ≤ L i , j ≤ D i , j ≤ 10 0 \leq L_{i,j} \leq D_{i,j} \leq 10 0Li,jDi,j10
  • 对于 60 % 60\% 60% 的评测用例, 1 ≤ n ≤ 50 1 \leq n \leq 50 1n50 0 ≤ L i , j ≤ D i , j ≤ 1 0 5 0 \leq L_{i,j} \leq D_{i,j} \leq 10^5 0Li,jDi,j105
  • 对于所有评测用例, 1 ≤ n ≤ 100 1 \leq n \leq 100 1n100 0 ≤ L i , j ≤ D i , j ≤ 1 0 5 0 \leq L_{i,j} \leq D_{i,j} \leq 10^5 0Li,jDi,j105 0 ≤ Q ≤ 2 31 − 1 0 \leq Q \leq 2^{31} - 1 0Q2311

蓝桥杯 2022 国赛 A 组 F 题。

解题思路:

emm这题虽然描述有点长,但实际思路并不难想

因为对于 P P P的定义已经给出很明显的提示了——多源最短路径,用Floyd

然后就是本题的图的特殊之处:灰尘度的变化

这个特殊之处直接导致了想一次Floyd直接解决问题是不可能的

因为Floyd得到的最短路径抽象去了路径上的点,那样就不知道哪条最短路径会缩短了

所以要想其他办法

去思考本题的答案会发现:

1)天数越多,就越可能达标

2)本题要求的是最少需要多少天

这不就是二分法

然后就明白了大概的解题思路:二分搜索天数,用Floyd判断这天的灰尘度是否达标

接下来就是一些细节的问题了,例如

对于完全图用二维数组存图、每轮搜索之前都需要根据天数初始化图的边权

计算得到天数的范围是 0 0 0~ 1 0 7 10^7 107

以及最重要的,关于数据范围的问题

q q q的最大值已经超出了int所能达到的精度,应该用long long存储

即使是long long,累加结束之后也可能溢出( 边权和 m a x = 1 5 3 边权和max = 1^{5^3} 边权和max=153),故需要加上正数判断

说了这么多,最后,AC代码如下

//Floyd
#include <iostream>
using namespace std;
const int max_n = 100;
const int max_l = 1e5;
const int max_d = 1e5;
const long long max_q = 0xFFFFFFFF - 1;
const int NaN = 0x3F3F3F3F;

int map[max_n][max_n] = { 0 };//存图
int limit[max_n][max_n] = { 0 };//最小灰尘度
int ans = -1;
int n;
long long q;
int dist[max_n][max_n] = { 0 };//临时图

bool floyd(int day) {
	for (int i = 0; i < n; i++) {//初始化
		for (int j = 0; j < n; j++) {
			dist[i][j] = max(map[i][j] - day / n - (day % n >= i + 1 ? 1 : 0), limit[i][j]);
		}
	}

	for (int i = 0; i < n; i++) {//Floyd
		for (int j = 0; j < n; j++) {
			for (int z = 0; z < n; z++) {
				dist[j][z] = min(dist[j][z], dist[j][i] + dist[i][z]);
			}
		}
	}

	long long sum = 0;
	for (int i = 0; i < n; i++) {//累加
		for (int j = 0; j < n; j++) {
			sum += dist[i][j];
		}
	}
	if (sum >= 0 && sum <= q) {//溢出判断 && 达标
		ans = day;
		return true;
	}
	else return false;
}

void bin_search() {//二分
	int left = -1, right = max_n * max_l + 1;
	while (left + 1 != right) {
		int middle = (left + right) / 2;
		if (floyd(middle)) {
			right = middle;
		}
		else {
			left = middle;
		}
	}
}

int main() {
	cin >> n >> q;
	for (int i = 0; i < n; i++)
		for (int j = 0; j < n; j++)
			cin >> map[i][j];
	for (int i = 0; i < n; i++)
		for (int j = 0; j < n; j++)
			cin >> limit[i][j];

	bin_search();
	cout << ans;
	return 0;
}

跑步(C++,01背包方案数,DP)

题目描述

路人甲准备跑 n n n 圈来锻炼自己的身体,他准备分多次( > 1 \gt1 >1)跑完,每次都跑正整数圈,然后休息下再继续跑。

为了有效地提高自己的体能,他决定每次跑的圈数都必须比上次跑的多。

可以假设他刚开始跑了 0 0 0 圈,那么请问他可以有多少种跑完这 n n n 圈的方案?

输入格式

一行一个整数,代表 n n n

输出格式

一个整数表示跑完这 n n n 圈的方案数。

样例 #1

样例输入 #1

212

样例输出 #1

995645335

提示

数据规模与约定

对于 100 % 100\% 100% 的数据,保证 5 ≤ n ≤ 500 5\le n\le 500 5n500

解题思路:

首先,这是一道01背包求方案数问题,接下来说明为什么

1)可以把要跑的圈数 n n n看作背包容量

2)可以把 1 1 1~ n n n圈看作 n n n个不同体积的物品

3)由于要跑的圈数必须一次多于一次,所以对于每个物品,只有选和不选两种情况

那么如何解决01背包求方案数问题呢?

我们先从**最简单的dfs**开始

int dfs(int w, int n) {//w为当前物品,n为背包容量
	if (n == 0) return 1;//找到方案
	if (w == 0) return 0;//未找到

	if (n >= w) return dfs(w - 1, n - w) + dfs(w - 1, n);//选 + 不选
	else return dfs(w - 1, n);//不选
}

思路是不是很简单?但是效率也很感人,所以我们需要进行优化

最容易想到的就是记忆了:wn一定,dfs(w, n)一定

int mem[max_n + 1][max_n + 1] = {0};//记忆

int dfs(int w, int n) {//w为当前物品,n为背包容量
	if (n == 0) return 1;//找到方案
	if (w == 0) return 0;//未找到

	if (mem[w][n]) return mem[w][n];//记忆
	if (n >= w) return mem[w][n] = (dfs(w - 1, n - w) + dfs(w - 1, n));//选 + 不选
	else return mem[w][n] = dfs(w - 1, n);//不选
}

当然,优化力度还是不够

注意到这个问题存在最优子结构,所以我们可以动态规划

for (int i = 0; i <= n; i++) mem[i][0] = 1;//初始化
for (int i = 1; i <= n; i++) {//i号物品
	for (int j = 1; j <= n; j++) {//j背包容量
		if (j >= i) mem[i][j] = mem[i - 1][j] + mem[i - 1][j - i];
		else mem[i][j] = mem[i - 1][j];
	}
}

现在时间上已经可以通过了,接下里进行空间优化

背包问题的空间优化自然少不了滚动数组

注意到j < i时,有mem[i][j] = mem[i - 1][j],所以可以不修改直接合并为一行

注意到j >= i时,有mem[i][j] = mem[i - 1][j] + mem[i - 1][j - i],即同列的上一行元素+同行左侧元素

故循环式可以修改为

for (int i = 1; i <= n; i++)
	for (int j = n; j >= i j--)
		mem[j] += mem[j - i];

至此,01背包求方案数问题解决了,AC代码如下

还有一件事,十年OI一场空,不开long long见祖宗,记得要防止溢出qwq

#include <iostream>
using namespace std;
const int max_n = 500;

long long mem[max_n + 1] = { 0 };//记忆

int main() {
	mem[0] = 1;//初始化
	int n;
	cin >> n;
	for (int i = 1; i <= n; i++)
		for (int j = n; j >= i; j--)
			mem[j] += mem[j - i];
	cout << mem[n] - 1;
	return 0;
}

(为什么要cout << mem[n] - 1;?记得回去看一眼题目哦)

遗址(C++,搜索)

题目描述

很久很久以前有一座寺庙,从上往下看寺庙的形状正好是一个正方形,由 4 4 4 个角上竖立的圆柱搭建而成。现在圆柱都倒塌了,只在地上留下圆形的痕迹,可是现在地上有很多这样的痕迹,专家说一定是最大的那个。

写一个程序,给出圆柱的坐标,找出由 4 4 4 个圆柱构成的最大的正方形,因为这就是寺庙的位置,要求计算出最大的面积。注意正方形的边不一定平行于坐标轴。

例如图有 10 10 10 根柱子,其中 ( 4 , 2 ) , ( 5 , 2 ) , ( 5 , 3 ) , ( 4 , 3 ) (4,2),(5,2),(5,3),(4,3) (4,2),(5,2),(5,3),(4,3) 可以形成一个正方形, ( 1 , 1 ) , ( 4 , 0 ) , ( 5 , 3 ) , ( 2 , 4 ) (1,1),(4,0),(5,3),(2,4) (1,1),(4,0),(5,3),(2,4) 也可以,后者是其中最大的,面积为 10 10 10

输入格式

第一行包含一个 N ( 1 ≤ N ≤ 3000 ) N(1\leq N\leq 3000) N(1N3000),表示柱子的数量。

接下来 N N N 行,每行有两个空格隔开的整数表示柱子的坐标(坐标值在 0 0 0 5000 5000 5000 之间),柱子的位置互不相同。

输出格式

如果存在正方形,输出最大的面积,否则输出 0 0 0

样例 #1

样例输入 #1

10
 9 4
 4 3
 1 1
 4 2
 2 4
 5 8
 4 0
 5 3
 0 5
 5 2

样例输出 #1

10

提示

【数据范围】

30 % 30\% 30% 满足: 1 ≤ N ≤ 100 1\leq N \leq100 1N100

60 % 60\% 60% 满足: 1 ≤ N ≤ 500 1\leq N \leq500 1N500

100 % 100\% 100% 满足: 1 ≤ N ≤ 3000 1\leq N \leq3000 1N3000

解题思路:

思路很简单:枚举每一个正方形,找出其中 S S S最大的

问题在于如何枚举每一个正方形

我们可以通过两个点来确认一个正方形

原理:正方形任意一条边的两个端点的横坐标之差、纵坐标之差均相等

在这里插入图片描述
AC代码如下

#include <iostream>
using namespace std;
const int max_x = 5000;
const int max_y = 5000;
const int max_n = 3000;

bool map[max_x * 2 + 1][max_y * 2 + 1] = { false };//存图
struct node { int x, y; }nodes[max_n + 1];//枚举节点
long long ans = 0;

long long calc_S(node n_1, node n_2) {//计算面积
	return (long long)((n_1.x - n_2.x) * (n_1.x - n_2.x)) +
		(long long)((n_1.y - n_2.y) * (n_1.y - n_2.y));
}

void search_max(const int n) {
	for (int i = 1; i <= n; i++) {//枚举正方形
		for (int j = 1; j <= n; j++) {
			int x_1 = nodes[i].x, y_1 = nodes[i].y;
			int x_2 = nodes[j].x, y_2 = nodes[j].y;
			int t_1 = x_1 - x_2, t_2 = y_1 - y_2;
			if (x_1 + t_2 < 0 || y_1 - t_1 < 0 || x_2 + t_2 < 0 || y_2 - t_1 < 0) continue;//越界判断
			if (map[x_1][y_1] && map[x_2][y_2] && map[x_1 + t_2][y_1 - t_1] && map[x_2 + t_2][y_2 - t_1])
				ans = max(ans, calc_S(nodes[i], nodes[j]));
		}
	}
}

int main() {
	int n, x, y;
	cin >> n;
	for (int i = 1; i <= n; i++) {//存图
		cin >> x >> y;
		nodes[i].x = x; nodes[i].y = y;
		map[x][y] = true;
	}
	search_max(n);
	cout << ans;
	return 0;
}

注:1)图需要开大一些,防止正越界

2)枚举正方形时需要加上判断,防止负越界

3)计算面积无需开根号,勾股定理结果为边长的平方(即为 S S S

4)计算结果需要是long long数据类型

[蓝桥杯 2021 省 AB] 砝码称重(C++,01背包可行性)

题目描述

你有一架天平和 N N N 个砝码, 这 N N N 个砝码重量依次是 W 1 , W 2 , ⋯   , W N W_{1}, W_{2}, \cdots, W_{N} W1,W2,,WN 。 请你计算一共可以称出多少种不同的重量?

注意砝码可以放在天平两边。

输入格式

输入的第一行包含一个整数 N N N

第二行包含 N N N 个整数: W 1 , W 2 , W 3 , ⋯   , W N W_{1}, W_{2}, W_{3}, \cdots, W_{N} W1,W2,W3,,WN

输出格式

输出一个整数代表答案。

样例 #1

样例输入 #1

3
1 4 6

样例输出 #1

10

提示

【样例说明】

能称出的 10 种重量是: 1 、 2 、 3 、 4 、 5 、 6 、 7 、 9 、 10 、 11 1 、 2 、 3 、 4 、 5 、 6 、 7 、 9 、 10 、 11 123456791011

1 = 1 2 = 6 − 4 (  天平一边放  6 ,  另一边放 4)  3 = 4 − 1 4 = 4 5 = 6 − 1 6 = 6 7 = 1 + 6 9 = 4 + 6 − 1 10 = 4 + 6 11 = 1 + 4 + 6 \begin{aligned} &1=1 \\ &2=6-4(\text { 天平一边放 } 6, \text { 另一边放 4) } \\ &3=4-1 \\ &4=4 \\ &5=6-1 \\ &6=6 \\ &7=1+6 \\ &9=4+6-1 \\ &10=4+6 \\ &11=1+4+6 \end{aligned} 1=12=64( 天平一边放 6, 另一边放 4) 3=414=45=616=67=1+69=4+6110=4+611=1+4+6

【评测用例规模与约定】

对于 50 % 50 \% 50% 的评测用例, 1 ≤ N ≤ 15 1 \leq N \leq 15 1N15

对于所有评测用例, 1 ≤ N ≤ 100 , N 1 \leq N \leq 100, N 1N100,N 个砝码总重不超过 1 0 5 10^5 105

蓝桥杯 2021 第一轮省赛 A 组 F 题(B 组 G 题)。

解题思路:

对于任意一个砝码,有三种情况:

1)不放

2)放左侧(与物品同侧)

3)放右侧(与物品异侧)

我们从n号砝码开始,遍历每一种情况,也就是暴力搜索DFS

void dfs(int i, int sum) {//前i号物品
	if (i == 0) {
		if (sum > 0) {
			if (!book[sum]) {
				ans++;
				book[sum] = true;
			}
		}
		return;
	}

	dfs(i - 1, sum);//不选
	dfs(i - 1, sum - w[i]);//选,左侧
	dfs(i - 1, sum + w[i]);//选,右侧
}

接下来进行时间优化,最容易想到的是记忆

记忆策略:isum相同,停止搜索直接return(剪枝)

因为sum < 0会发生数组越界,所以初始调用dfs(n, max_w)加上一个偏移量即可

void dfs(int i, int sum) {//前i号物品
	if (book[i][sum]) return;
	book[i][sum] = true;

	if (i == 0) {
		if (sum > max_w) ans++;
		return;
	}

	dfs(i - 1, sum);//不选
	dfs(i - 1, sum - w[i]);//选,左侧
	dfs(i - 1, sum + w[i]);//选,右侧
}

对于本题的数据,加上记忆之后的代码已经够用了,但是我们仍可以进一步优化

方法仍然是背包问题中常见的动态规划

但从当前写出来的DFS是看不出如何动态规划的,所以我们修改一下,尝试理解下面的代码

void dfs(int i, int sum) {//前i号物品
	book[sum] = true;
	if (i == 0) return;

	dfs(i - 1, sum);//不选
	dfs(i - 1, sum - w[i]);//选,左侧
	dfs(i - 1, sum + w[i]);//选,右侧
}

最后输出答案的步骤如下

dfs(n, sum);
for (int i = sum + 1; i <= 2 * sum; i++) ans += book[i];
cout << ans;

并不难懂吧qwq

那么接下来所谓的动态规划,其实就是把没有返回后操作的递归,变成循环而已

/* 大多数没有返回后操作的递归,都可以转化为循环的形式 */

book[0][sum] = true;//初始化
for (int i = 1; i <= n; i++) {
	for (int j = 0; j <= 2 * sum; j++) {
		if (book[i - 1][j]) book[i][j] = true;//不选
		else if (j - w[i] >= 0 && book[i - 1][j - w[i]]) book[i][j] = true;//选,右侧
		else if (j + w[i] <= 2 * sum && book[i - 1][j + w[i]]) book[i][j] = true;//选,左侧
	}
}

要注意到动态规划的状态转移方向与递归是相反的

然后还可以把循环主体的部分的分支结构改为顺序结构,进一步提升效率

for (int i = 1; i <= n; i++) {
	for (int j = 0; j <= 2 * sum; j++) {
		book[i % 2][j] = (book[(i - 1) % 2][j]) || //不选
			(j - w[i] >= 0 && book[(i - 1) % 2][j - w[i]]) || //选,左侧
			(j + w[i] <= 2 * sum && book[(i - 1) % 2][j + w[i]]);//选,右侧
	}
}

接下来,就是背包问题中很常见的滚动数组了,利用i % 2的性质把数组压缩到二维

AC代码如下

//01背包可行性
#include <iostream>
using namespace std;
const int max_n = 100;
const int max_w = 1e5;

int w[max_n + 1] = { 0 };//砝码
bool book[2][2 * max_w + 1] = { false };//滚动数组
int ans = 0;

int main() {
	int n, sum = 0;
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> w[i];
		sum += w[i];
	}
	book[0][sum] = true;//初始化
	for (int i = 1; i <= n; i++) {
		for (int j = 0; j <= 2 * sum; j++) {
			book[i % 2][j] = (book[(i - 1) % 2][j]) || //不选
				(j - w[i] >= 0 && book[(i - 1) % 2][j - w[i]]) || //选,左侧
				(j + w[i] <= 2 * sum && book[(i - 1) % 2][j + w[i]]);//选,右侧
		}
	}

	for (int i = sum + 1; i <= 2 * sum; i++) ans += book[n % 2][i];
	cout << ans;
	return 0;
}

这样就从递归时的 132 132 132ms -> 动态规划时的 88 88 88ms

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/157983.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

工控安全—工控常见协议识别

文章目录一、Nmap常见参数介绍二、工控常见协议识别三、工控设备指纹识别3.1 S73.2 Modbus3.3 IEC 60870-5-1043.4 DNP33.5 EtherNet/IP3.6 BACnet3.7 Tridium Niagara Fox3.8 Crimson V33.9 OMRON FINS3.10 PCWorx3.11 ProConOs3.12 MELSEC-Q四、测试一、Nmap常见参数介绍 -s…

STM32F411CE驱动Xbox摇杆

外观 引脚说明和原理 GND-GND 5V-5V VRX-ADC1通道1 VRX-ADC1通道2 SW独立按键-单片机的输入检测 本质上这个遥感就是集成了一个按键和两个电位器&#xff0c;遥感转动改变电位器也会转动&#xff0c;电压输出的值也就不一样&#xff0c;通过检测数值可自定义的做出判断&a…

linux发送tcp/udp请求

本文章介绍下通过nc工具和iperf工具&#xff0c;发送tcp/udp请求一、nc工具&#xff08;netcat工具&#xff09;这个工具linux系统默认是自带的&#xff0c;以下是命令的常用参数1.1 发送tcp请求在服务端监听端口nc -l port客户端连接并发送请求nc -v host port在服务端收到了信…

javaWeb 会话和跟踪

会话 用户打开浏览器&#xff0c;访问web服务器的资源&#xff0c;会话建立&#xff0c;直到有一方断开连接&#xff0c;会话结束。在一次会话中可以包含多次请求和响应。 会话跟踪 HTTP协议是无状态的&#xff0c;每次浏览器向服务器请求时&#xff0c;服务器都会将该请求视…

kafka消费者API

kafka的消费方式 pull&#xff08;拉模式&#xff09; 消费者采用从broker中主动拉去数据 kafka采用这种方式 push&#xff08;推模式&#xff09; kafka没有采用这种方式&#xff0c;因为由broker决定消费发送速率。很难适应所有消费者 pull模式不足之处是&#xff0c;如…

Linux 的 Vim,gcc,makefile 的使用

坚持看完&#xff0c;结尾有思维导图总结 这里写目录标题vimvim的安装vim的配置vim 的使用vim 的三种模式三种模式对应的命令通用命令模式底行模式gcc 和 g编译和执行预编译编译汇编链接过程make总结vim Vim 是Linux 中使用的编辑器&#xff0c;一般的程序要经历一个过程才能运…

Codeforces Round #841 (Div. 2) and Divide by Zero 2022(A~E)

A. Joey Takes Money给出一个序列a&#xff0c;每次操作可以选择两个数&#xff0c;将两个数分别改成与原数乘积相同的两个数&#xff0c;问最后得到的最大的数组和是多少。思路&#xff1a;乘积一定&#xff0c;和最大一定是与1相乘。则整个数组的积与n - 1个1的和就是最大值。…

探索云原生技术之容器编排引擎-Kubernetes/K8S详解(7)

❤️作者简介&#xff1a;2022新星计划第三季云原生与云计算赛道Top5&#x1f3c5;、华为云享专家&#x1f3c5;、云原生领域潜力新星&#x1f3c5; &#x1f49b;博客首页&#xff1a;C站个人主页&#x1f31e; &#x1f497;作者目的&#xff1a;如有错误请指正&#xff0c;将…

缓存(redis)与数据库(MYSQL)数据一致性问题

在MYSQL数据库集文章中&#xff0c;仔细的学习了一些MYSQL数据库的知识。但是&#xff0c;随着我们的业务越来越好&#xff0c;那么我们不可能直接去操作MYSQL数据库。因为直接去操作MYSQL终究会有比较多的I/O操作&#xff0c;而使整个系统的性能最终受到数据库I/O的制约而无法…

教外篇(6):C++ qrencode 实现二维码生成

系列文章目录 文章目录 系列文章目录前言一、qrencode库的基本使用二、BMP图片生成原理三、二维码生成四、放大图像、解决编码问题前言 该系列教程目录与说明可以查看这篇文章::C/C++教程 本文主要介绍如何使用C++来实现二维码的生成,使用到了开源库:qrencode 代码生成结…

C++入门--vector

目录 vector的介绍 vector的使用 对象的定义 遍历 reserve与resize insert与erase 迭代器失效 vector的模拟实现 vector的介绍&#xff1a; vector是表示可变大小数组的序列容器。 vector的使用&#xff1a; 对象的定义&#xff1a; void test_vector1() {vector<int…

ZYNQ图像-腐蚀膨胀笔记

大磊fpga 腐蚀 下图从左到右依次为a&#xff0c;b&#xff0c;c step1&#xff1a;将b中的黄色十字架在a中遍历 step2&#xff1a;当b的黄色方格在a中 没有碰到白色方格 时输出中心坐标 step3&#xff1a;将step2中所有输出的坐标涂成黄色&#xff0c;得出c图 膨胀 step1…

Redhat 7 安装 iftop软件

1.关闭subscription-manager vi /etc/yum/pluginconf.d/subscription-manager.conf enable 0 2.通过浏览器下载Centis-7.repo http://mirrors.aliyun.com/repo/Centos-7.repo 3.上传至/etc/yum.repos.d/ 4.修改Centos-7.repo文件 #cd /etc/yum.repos.d/ #ls #vim CentOS…

怎么看电脑是32位还是64位?超级简单的方法!

熟悉计算机的朋友都知道&#xff0c;电脑系统可以分为32位和64位系统。它们之间有什么区别&#xff1f;它们支持不同的内存&#xff1a;32位操作系统最多支持4G内存&#xff0c;但64位系统可以支持4G、8G、16G、32G、64G、128G、256G等。兼容软件也不同&#xff1a;32位只支持3…

老照片修复方法是什么?这篇文章来告诉你

我们每年回老家时&#xff0c;都喜欢看看以前的老物件&#xff0c;尤其是照片&#xff0c;因为它承载了我们一代又一代人的回忆&#xff0c;不管过去了多久&#xff0c;家里的长辈拿到一张照片时&#xff0c;都可以准确的说出当时在哪里&#xff1f;在做什么&#xff1f;由此引…

基于python和cv2、pytorch实现的车牌定位、字符分割、字符识别项目

问题描述车牌的检测和识别的应用非常广泛&#xff0c;比如交通违章车牌追踪&#xff0c;小区或地下车库门禁。在对车牌识别和检测的过程中&#xff0c;因为车牌往往是规整的矩形&#xff0c;长宽比相对固定&#xff0c;色调纹理相对固定&#xff0c;常用的方法有&#xff1a;基…

linux C -- 内存管理

链接: linux C学习目录 linux C 共享内存机制共享内存物理位置shared memory常用函数编程模型范例write.cread.c修改参数实验共享内存 二个或者多个进程,共享同一块由系统内核负责维护的内部内存区域其地址空间通常被映射到堆和栈之间无需复制信息,最快的一种IPC机制需要考虑同…

web应用 —— HTML

web应用 一、HTML 1.插件 1.Live Server 模拟网站服务器 2.Auto Rename Tag 自动修改标签对 3.设置settings-format-勾选Format On Save &#xff08;创建文件&#xff1a;File-Open Folder-新建文件夹-命名文件&#xff09; 2.html文档结构 html所有标签为树形结构&…

基于YOLOv5+C3CBAM+CBAM注意力的海底生物[海参、海胆、扇贝、海星]检测识别分析系统

在我前面的一些文章中也有用到过很多次注意力的集成来提升原生检测模型的性能&#xff0c;这里同样是加入了注意力机制&#xff0c;区别在于&#xff0c;这里同时在两处加入了注意力机制&#xff0c;第一处是讲CBAM集成进入原生的C3模块中&#xff0c;在特征提取部分就可以发挥…

Microsoft系统漏洞修复

近期收到服务器系统漏洞扫描&#xff0c;发现很多关于Microsoft本身的系统漏洞。 有很多新手不知道怎么去修复系统漏洞&#xff0c;害怕一旦修复出问题&#xff0c;自己要担责。 我这里讲解下怎么准备的去寻找漏洞&#xff0c;并把它修复的过程。 我已下列的漏洞为例&#x…