C++题目大总结(持续更新中)

本文总结了C++编程竞赛中的搜索、数位DP、思维/数学和最短路等题型,包括城市距离、手机号码、I Hate 1111、Marbles、逛公园等题目,通过实例解析了广度优先搜索、数位动态规划、思维策略和最短路径算法,附带AC代码。
摘要由CSDN通过智能技术生成


备注:题目后的难度评价是这样制定的
1.若 l u o g u luogu luogu 上有难度评价(除 暂无评价 外),则为 l u o g u luogu luogu 上的难度评价。
2.否则,作者将参考其他类似题目的难度评价与个人主观来评价其难度。

S

搜索

1.城市距离( 普 及 + / 提 高 \textcolor{green}{普及+/提高} +/

U p d Upd Upd 2022.11.28 2022.11.28 2022.11.28 完成
八中OJ链接
我们还是先观察数据范围: 1 ≤ n , m ≤ 500 1 \leq n,m \leq 500 1n,m500,那么就只能用广度优先搜索了。
个人认为这道题出得不好,因为按照目前的最优解,时间复杂度应该为 O ( n 2 m 2 ) O(n^2m^2) O(n2m2) 的,应该是过不掉的,但为什么在 150 m s 150ms 150ms 内就过掉了呢?原因有二:一是数据太水,仅有一组极限数据并且 ‘#’ 符号少得可怜。二是如果每一次 b f s bfs bfs 不用 O ( n m ) O(nm) O(nm) 的时间初始化,其实真正的时间复杂度是远远小于 O ( n 2 m 2 ) O(n^2m^2) O(n2m2) 的。
我们先来想一想最暴力的做法,枚举两个城市,再用 O ( n m ) O(nm) O(nm) 求其最短距离,这样做是 O ( n 3 m 3 ) O(n^3m^3) O(n3m3) 的,绝对会原地起飞。
现在,来说说这道题的正确做法——染色法,即将每个城市用 d f s dfs dfs 染色后去求解。如果每个城市只有一个 #,那么,这个问题就是一个很裸的 B F S BFS BFS 问题。但是,现在的问题就是,要如何
做,才能够求得一片城市与另一片城市的最短距离?其实,每一次 b f s bfs bfs 最坏会遍历整个地图,并且我们只是求其最小距离,所以,我们可以将某一个城市内的所有 # 全都作为我们搜索的起点,然后计算从这些起点当中,走到另一个 # 的最短距离是多少( B F S BFS BFS),因为我们只需要找到最小距离,并不需要知道究竟是哪个城市到了另外哪个城市,因此,我们在跑 B F S BFS BFS 之前,找到当前我们找的这个城市有哪些 #,将同一个城市内的所有#都 p u s h push push 进我们的队列当中,然后,再跑一个 B F S BFS BFS,找到这个城市能走到的离它最近的城市的距离,最后取最小的那个答案即可。
A C AC AC C o d e : Code: Code:

#include <bits/stdc++.h>
using namespace std;
const int N = 5e2 + 5, INF = 0x3f3f3f3f;
const int zx[4] = {
   -1, 0, 1, 0}, zy[4] = {
   0, 1, 0, -1};
int n, m, ans = INF;
char c[N][N];
bool vis[N][N];
struct node {
   
	int x, y, step;
};
queue<node> q;
bool in(int x, int y) {
   
	return x >= 1 && x <= n && y >= 1 && y <= m;
}
void dfs(int x, int y) {
   
	c[x][y] = 'A';
	q.push((node){
   x, y, 0});
	for(int i = 0;i < 4;i++) {
   
		int dx = x + zx[i], dy = y + zy[i];
		if(in(dx, dy) && c[dx][dy] == '#') dfs(dx, dy);
	}
}
void bfs() {
   
	for (int i = 1;i <= n;i++)
		for (int j = 1;j <= m;j++)
			vis[i][j] = 0;
	while(!q.empty()) {
   
		node p = q.front();
		q.pop();
		if(c[p.x][p.y] == '#') {
   
			while(!q.empty()) q.pop();
			ans = min(ans, p.step);
			return ;
		}
		for(int i = 0;i < 4; ++i) {
   
			int dx = p.x + zx[i], dy = p.y + zy[i];
			if(in(dx, dy) && !vis[dx][dy] && c[dx][dy] != 'A') {
   
				vis[dx][dy] = 1;
				q.push((node){
   dx, dy, p.step + 1});
			}
		}
	}
}
int main() {
   
	scanf("%d%d", &n, &m);
	for(int i = 1;i <= n;i++) scanf("%s", c[i] + 1);
	for(int i = 1;i <= n;i++)
		for(int j = 1;j <= m;j++)
			if(c[i][j] == '#') {
   
				dfs(i, j);
				bfs();
			}
	printf("%d", ans);
	return 0;
} 

数位DP

1.手机号码(CQOI2016,, 省 选 / N O I − \textcolor{purple}{省选/NOI-} /NOI

U p d Upd Upd 2022.11.26 2022.11.26 2022.11.26 完成
洛谷链接 八中OJ链接
看到数据范围 1 0 10 ≤ L ≤ R ≤ 1 0 11 10^{10} \leq L \leq R \leq 10^{11} 1010LR1011 并且是统计 [ L , R ] [L,R] [L,R] 内有满足条件的号码数量,很容易想到数位 D P DP DP
其实,数位 D P DP DP 也是一个很模板化的东西。对其总结如下:

dfs(数的最后若干位,各种限制条件,当前第几位)
	if 最后一位
    	return 各种限制条件下的返回值
    局部变量 ct = 当前位的数字
    局部变量 sum = 0;
    for i = 0 to ct - 1
    	sum += 当前位取i时一定无无限制的合法状态数
        sum += 当前位取i时满足当前限制的合法状态数
    根据ct更新限制条件 不再满足则 return sum
    return sum + dfs(当前位后的若干位,更新后的限制条件,下一位)

slv(当前数)
	if (只有一位) return 对应的贡献
    局部变量 ct;
    for ct = 可能最高位 to 1
    	if 当前位有数字 break
    局部变量 nw = 当前位数字
    局部变量 sum = 0
    for i = 1 to nw - 1
    	sum += 当前位取i后合法情况任意取的贡献
    for i = 1 to ct-1
    	for j = 1 to 9
        	sum +=  第i位取j后合法情况任意取的贡献
    sum += dfs(去掉第一位后的若干位,限制条件,第二位)
    return sum

main
	预处理当前位取i的各种条件各种限制的贡献
    读入 L R
    --L
    输出 slv(R)-slv(L)
    return 0

而在这道题中,我们就可以很容易的套模板,定义六维的 D P DP DP 去记忆化 d f s dfs dfs,即 d p [ 第 i 位 ] [ 填 j ] [ 目 前 连 续 L ] [ 是 否 有 4 ] [ 是 否 有 8 ] [ 是 否 有 至 少 3 位 连 续 数 字 ] dp[第i位][填j][目前连续L][是否有4][是否有8][是否有至少3位连续数字] dp[i][j][L][4][8][3]
然后按照板子按部就班的做便能 A C AC AC.
A C AC AC C

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值