当前位置: 首页 > wzjs >正文

网站cms建设中国建设网上银行个人登录

网站cms建设,中国建设网上银行个人登录,深圳电力建设公司,帮别做网站目录 前言 奶牛选美 分析 代码 大臣的旅费 分析 代码 飞机降落 分析 代码 母亲的牛奶 分析 代码 扫雷 分析 代码 前言 虽为诞辰,但也不忘完成每日的训练。 今天给大家带来五道dfs的题目,包括组合数,连通块,数的…

目录

前言

奶牛选美

分析

代码

大臣的旅费

分析

代码

飞机降落

分析

代码

母亲的牛奶

分析

代码

扫雷

分析

代码


前言

虽为诞辰,但也不忘完成每日的训练。

今天给大家带来五道dfs的题目,包括组合数,连通块,数的直径等方面的内容。

因为时间较为仓促,很多地方可能讲的不是很全面,请见谅。


奶牛选美

分析

题目保证图中存在且仅存在两个连通块,说实话这道题挺水的,不知道为什么难度是中等。

第一步,dfsbfs找出两个连通块。

随后的问题就是求两个连通块的最小曼哈顿距离,枚举两个连通块中的点,随后取最小值即可(这题题目的结果要减一)。

abs(x1 - x2) + abs(y1 - y2)

最后我们来分析一下时间复杂度,dfs或bfs的时间复杂度都是O(n ^ 2),求曼哈顿距离的时间复杂度是O(x * (n - x))x表示第一个连通块中的连通块数量),可以发现这是一个基本不等式,最大值为(n ^ 2) / 4,所以总的时间复杂度就是O(n ^ 2)。通过本题绰绰有余。


代码

// dfs连通块
#include<iostream>
#include<cstring>
#include<vector>
#include<queue>
#define s second
#define f first
using namespace std;
typedef pair<int, int> PII;
const int N = 55;
int n, m;
char map[N][N];
bool read[N][N];
vector<PII> v[3];
int cnt;
int l = 0x3f3f3f3f;
PII s[] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};void dfs(int x, int y, vector<PII>& v)
{read[x][y] = true;v.push_back({x, y});for(int i = 0; i < 4; i++)if(map[x + s[i].f][y + s[i].s] == 'X' && !read[x + s[i].f][y + s[i].s])dfs(x + s[i].f, y + s[i].s, v);
}int main()
{scanf("%d%d", &n, &m);for(int i = 1; i <= n; i++)scanf("%s", map[i] + 1);for(int i = 1; i <= n; i++)for(int j = 1; j <= m; j++)if(map[i][j] == 'X' && !read[i][j])dfs(i, j, v[cnt++]);for(PII a : v[0])for(PII b : v[1]){//printf("111");l = min(l, abs(a.s - b.s) + abs(a.f - b.f));}printf("%d", l - 1);return 0;
}

大臣的旅费


分析

从题目的描述中可以知道题目给的是一颗树。所以我们每次读取的边的数量就是n - 1(这个好像不用我说题目也说了)。而根据题目描述的所花路费只与总路程有关,路费是一个等差数列,所以对于这道题我们只需要求出路中的最长路径随后用n项和公式计算即可。

那么怎样求树中的最长路径呢?树的最长路径,其实就是树的直径

树的直径问题解法一边是使用两次dfs,一次找到离根节点(对于双向的树来说这个根可以是树中的任何点)最远的点,随后再用一次dfs遍历出的最长路径就是树的直径。

如何证明呢?

我们先来思考一下从一个点遍历一遍这棵树又回到起点的路径是多长,显而易见,这个路径长是边长和的二倍,在这条路中每条边是都被走了两遍的。

我们若想找到树中的直径显然是不能重复经过一条边的,所以直径要小于边长和的两倍

我们再思考,直径的理想情况是遍历树中的每一条边,这种情况是有可能的(一条直线),所以树的直径要小于等于树的边长和

铺垫完了这些我们就可以将求树的直径转化成树的边长和减去不经过的边长的长度,也就是:

d = l - x

d表示直径,l表示树边长之和,x表示未经过的边长,那么问题就转化成了求x的最小值

那么如何来求这个最小值呢?为方便理解,主包画了一个草图。

可以明显的看出直径就是最长的那一条(这是废话)。

我们按照我们的算法思路先以A为根节点找到D随后再一遍dfs找到C,那么从DC就是树的直径

为何这样是正确的呢?其实不难想,因为我们前面的分析,我们要求直径其实就是求避开的边长的最小值。

而我们在遍历树的过程中每次只记录距离最长的一条边(类似于dp),自然而然的就避开了所有短的边。

又根据树的性质两点之间的路径是唯一的,并且树中不存在环。所以就不存在图中那些弯弯绕,每次丢弃权重最小的一部分即可。

时间复杂度不必多说,两次都是O(n)


代码

/*树中的最长路,树的直径问题。先求长度,然后公式求解
*/
#include<iostream>
#include<vector>
#include<cstring>
#define s second
#define f first
using namespace std;
typedef pair<int, int> PII;
typedef long long LL;
const int N = 100010;
int n;
vector<int> tree[N];
vector<int> w[N];
bool read[N];
PII dfs(int v)
{PII l = {0, v};read[v] = true;for(int i = 0; i < tree[v].size(); i++){int z = tree[v][i];if(!read[z]){PII m = dfs(z);if(m.f + w[v][i] > l.f)l = {m.f + w[v][i], m.s};}}return l; //最长距离
}int main()
{scanf("%d", &n);for(int i = 1; i < n; i++){int x, y, z;scanf("%d%d%d", &x, &y, &z);tree[x].push_back(y);w[x].push_back(z);tree[y].push_back(x);w[y].push_back(z); }int i = dfs(1).s;memset(read, false, sizeof read);i = dfs(i).f;//printf("%d", i);printf("%lld", ((LL)i * (11 + 10 + i))/2);return 0;
}

飞机降落


分析

第一眼感觉是贪心,但是看到数据量之后发现不是贪心,数据量很小所以我们考虑搜索状态压缩dp

初步分析,总共有n太飞机,每台飞机都要在固定的区间内降落。

发现n很小,我们可以考虑组合数,而组合数的时间复杂度是n * n!这道题就是36288000,三千六百万,再乘上t就是3.6亿,时间限制是两秒钟。

虽然组合数的常数很小可能通过但是也有TLE的风险,考虑优化。

优化不必多说,搜索的常见优化就是打表和剪枝,对于组合数问题打表显然不行,所以我们考虑剪枝

我们枚举组合数就是枚举每台飞机降落的顺序,显然后面的飞机不可能在前面的飞机降落前完成降落,所以剪枝的判断条件就是后面的飞机能否在前面的飞机完成降落后降落

还有一步贪心就是如果这台飞机可以降落,我们该选择哪个时间使其降落。很好想尽量让开始降落的时间靠前(和线性dp很像)因为降落时间早的状态是包含降落时间晚的所有状态的。

剪枝后运行时间是38ms,说明我们的代码效率还是很高的。(实际剪枝后的时间复杂度是接近O(n ^ 2)的)


代码

/*全排列降落顺序即可
*/
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
const int N = 15;
int t, n;
int l[N], r[N], d[N];
vector<int> vtr;
bool read[N];bool dfs(int t) 
{if(vtr.size() == n)return true;for(int i = 1; i <= n; i++){if(!read[i]){if(l[i] + r[i] >= t) //可以放下{read[i] = true;vtr.push_back(i);if(dfs(max(t, l[i]) + d[i])) return true;read[i] = false;vtr.pop_back();}}}return false;
}
int main()
{scanf("%d", &t);while(t--){memset(read, false, sizeof read);vtr.clear();scanf("%d", &n);for(int i = 1; i <= n; i++)scanf("%d%d%d", l + i, r + i, d + i);if(dfs(0))puts("YES");elseputs("NO");}}

母亲的牛奶


分析

不得不吐槽一下这个名字好怪……

看完题目后发现数据量很小,分析一下能不能直接用搜索

初步分析的话是可以的,但是我们发现一个问题,那就是不知道搜索什么时候结束。

这个简单,因为每次倒牛奶都不会有损失也不会有增加,所以根据某某定理(主包忘了),在之后某一个时间点的状态一定会和当前状态完全相同。

也就是一个循环,所以我们只需要存储一下每次遍历到的状态,当发现重复遍历时退出即可。

分析一下时间复杂度,我们分析极限情况

每个桶的大小是小于等于20的,也就是每个桶有21种状态。所以总的状态量就是21 * 21 * 21,大概是一万,通过本题绰绰有余。


代码

#include<iostream>
using namespace std;
const int N = 22;
bool read[N][N][N]; //dfs遇到重复状态时就退出
bool C[N]; 
int a, b, c;
void dfs(int x, int y, int z)
{if(read[x][y][z]) return;read[x][y][z] = true;if(!x)  C[z] = true;//printf("%d %d %d", x, y, z);if(x != 0){dfs(max(0, x - (b - y)), min(b, y + x), z);dfs(max(0, x - (c - z)), y, min(c, z + x));}if(y != 0){dfs(min(a, x + y), max(0, y - (a - x)), z);dfs(x, max(0, y - (c - z)), min(c, z + y));}if(z != 0){dfs(min(a, x + z), y, max(0, z - (a - x)));dfs(x, min(b, y + z), max(0, z - (b - y)));}}int main()
{scanf("%d%d%d", &a, &b, &c);dfs(0, 0, c);for(int i = 0; i < 21; i++)if(C[i])printf("%d ", i);return 0;
}

扫雷


分析

今天的最后一道题(因为主包以前做过千奇百怪的扫雷游戏所以这种题感觉闭着眼睛都能写),不得不感慨,刷那么多题没记住几个,写着玩的东西倒是记忆犹新。

数据量是100 * 300 * 300,大概是1e7,所以我们需要将时间复杂度控制在线性

怎么写呢?我们先根据每个雷的位置处理出每个点应该有的数字,随后我们可以将0看为连通块,随后我们发现我们在点击非零块的时候一次只能解开一个,而在点击0时每次可以解开多个块(包括部分非0块)。

所以我们的思路是先解开0的连通块,随后再统计没有解开的非零块的数量即可。


代码

/*先找全0的连通块
*/
#include<iostream>
#include<cstring>
#define s second
#define f first
using namespace std;
typedef pair<int, int> PII;
const int N = 310;
int t, n;
char map[N][N];
bool read[N][N];
int st[N][N];
PII s[] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}, {-1, -1}, {1, 1}, {-1, 1}, {1, -1}};void init()
{for(int i = 1; i <= n; i++)for(int j = 1; j <= n; j++)if(map[i][j] == '*')for(int k = 0; k < 8; k++)st[i + s[k].f][j + s[k].s]++; //统计数字
}void dfs(int x, int y)
{read[x][y] = true;if(st[x][y] == 0 && map[x][y] == '.'){for(int i = 0; i < 8; i++)if(!read[x + s[i].f][y + s[i].s]){read[x + s[i].f][y + s[i].s] = true;dfs(x + s[i].f, y + s[i].s); //dfs求连通块}}
}int main()
{scanf("%d", &t);for(int i = 1; i <= t; i++){memset(read, 0, sizeof read);memset(st, 0, sizeof st);scanf("%d", &n);for(int i = 1; i <= n; i++)scanf("%s", &map[i][1]);init();/*for(int i = 1; i <= n;puts(""), i++)for(int j = 1; j <= n; j++)printf("%d ", st[i][j]);*/int l = 0;for(int i = 1; i <= n; i++)for(int j = 1; j <= n; j++)if(map[i][j] == '.' && !read[i][j] && st[i][j] == 0){//printf("%d %d\n", i, j);dfs(i, j);l++;}//printf("%d\n", l); for(int i = 1; i <= n; i++)for(int j = 1; j <= n; j++)if(map[i][j] == '.' && !read[i][j])l++;printf("Case #%d: %d\n", i, l);}return 0;
}
http://www.dtcms.com/wzjs/807065.html

相关文章:

  • 个人网站建设方案书 范文包图网官网
  • 国外做自动化网站高性能的网站建设指南
  • 德州购物网站建设用ps怎么做网站导航条怎么做
  • 宁波住房城乡建设局网站中国建筑土木建设有限公司
  • 专门卖电子产品的网站域名买完后如何做网站
  • 数学建模网站建设哪里有做网站服务
  • 深圳网站制作招聘做策划的人经常浏览的网站
  • 成都网站的在手机里面开网店怎么开
  • 台州制作网站软件网站多语言 设计
  • pos机网站建设方案福建建设执业中心网站
  • 用什么开源框架做网站vue.js网站如果做自适应
  • 成都网页设计的网站建设wordpress链接 结尾
  • 吉隆坡建设大学中文网站建设信用卡购物网站
  • 网站设置密码进入成都网页设计设公司
  • 信用 网站 建设方案渠道网
  • 网站编程好学吗icp网站域名怎么填写
  • 纹理网站推荐17Z一起做网站广州站
  • 嘉兴城乡建设局门户网站网站提示宏
  • 网站后台更新能发锚文本的网站
  • 建设网站费用明细手表商城网站建设
  • 旅游网站排行榜前十名官网上海网站建设品
  • 温州建校网站wordpress开发的主流架构
  • 网上做家教那个网站好站长工具在线查询
  • 网页制作与网站建设...东道设计公司规模
  • 在线做简历的网站微信运营是干嘛的
  • 网站架构图文章生成器网页版
  • 设计网站策划书项目商业网站建设方案
  • 接单做网页的网站网站推广的方法和途径
  • 深圳手机模板建站网站建设的步骤图片过程
  • 网站建设的功能特点有哪些龙岩网站制作教程