蓝桥杯图专题
蓝桥杯图专题
【问题描述】
很久以前,T王国空前繁荣。为了更好地管理国家,王国修建了大量的快速路,用于连接首都和王国内的各大城市。
为节省经费,T国的大臣们经过思考,制定了一套优秀的修建方案,使得任何一个大城市都能从首都直接或者通过其他大城市间接到达。同时,如果不重复经过大城市,从首都到达每个大城市的方案都是唯一的。
J是T国重要大臣,他巡查于各大城市之间,体察民情。所以,从一个城市马不停蹄地到另一个城市成了J最常做的事情。他有一个钱袋,用于存放往来城市间的路费。
聪明的J发现,如果不在某个城市停下来修整,在连续行进过程中,他所花的路费与他已走过的距离有关,在走第x千米到第x+1千米这一千米中(x是整数),他花费的路费是x+10这么多。也就是说走1千米花费11,走2千米要花费23。
J大臣想知道:他从某一个城市出发,中间不休息,到达另一个城市,所有可能花费的路费中最多是多少呢?
【输入格式】
输入的第一行包含一个整数n,表示包括首都在内的T王国的城市数
城市从1开始依次编号,1号城市为首都。
接下来n-1行,描述T国的高速路(T国的高速路一定是n-1条)
每行三个整数Pi, Qi, Di,表示城市Pi和城市Qi之间有一条高速路,长度为Di千米。
输出格式
输出一个整数,表示大臣J最多花费的路费是多少。
样例输入1
5
1 2 2
1 3 1
2 4 5
2 5 4
样例输出1
135
【输出格式】
大臣J从城市4到城市5要花费135的路费。
【思路】
以每一个城市为起点暴力搜索其能到达的最远的城市,最大的结果即为所求。可以通过75%的数据。
注意=
由于是无向图,在存储数据的时候边双向关联的。以所给样例为例,若标记像往常dfs一样在for循环内标记,会出现从1结点进入2结点,然后2结点又遍历1结点的情况,1结点城市走了两遍,这是不允许的。标记应该在进入递归函数时。
package _2013;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
/**
* @author JohnnyLin
* @version Creation Time:2020年10月13日 下午12:45:32
* https://www.dotcpp.com/oj/problem1438.html3
*
*/
public class _大臣的旅费 {
static int n,ans;
static List [] node;
static int map[][];
static boolean vis[];
static void dfs(int father,int dist) {
//System.out.println(father+" "+dist);
vis[father]=true;
for(int i=0;i< node[father].size();i++) {
int son=node[father].get(i);
if(!vis[son]) {
//vis[son]=true; 错误写法
dist+=map[father][son];
if(dist>ans) {
ans=dist;
}
dfs(son, dist);
dist-=map[father][son];
//vis[son]=false; 错误写法
}
}
vis[father]=false;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
/*
012
11121314
*/
Scanner reader=new Scanner(System.in);
n=reader.nextInt();
map=new int[n+1][n+1];
node=new ArrayList[n+1];
vis=new boolean[n+1];
for(int i=1;i<=n;i++)
node[i]=new ArrayList<>();
for (int i = 1; i < n; i++) {
int p=reader.nextInt();
int q=reader.nextInt();
int d=reader.nextInt();
node[p].add(q);
node[q].add(p);
map[p][q]=d;
map[q][p]=d;
}
for (int i = 1; i <=n; i++) {
dfs(i, 0);
vis=new boolean[n+1];
}
System.out.println((21+ans)*ans/2);
}
}
另一种写法:
这题就是求一棵树上的距离最远的两个点的距离,也就是最长路径问题。
最长路径问题就是:
假设 s-t这条路径为树的直径,或者称为树上的最长路径。现有结论,从任意一点u出发搜到的最远的点一定是s、t中的一点,然后在从这个最远点开始搜,就可以搜到另一个最长路的端点,即用两遍广搜就可以找出树的最长路。
package _2013;
/**
* @author JohnnyLin
* @version Creation Time:2020年10月13日 下午2:52:17
*/
import java.util.ArrayList;
import java.util.Scanner;
public class _大臣的旅费1 {
public static int n;
static int Max=Integer.MIN_VALUE;
//代表最长距离的起点城市
static int point;
//城市结构体
//声明动态数组 ArrayList map1=new ArrayList<>();看成A数据类型 类似于Integer
//数组:A [] map2=new A();
//两句合在一起就相当于:声明了一个数组map2里面的数据元素是A类型的 而A类型的数据是edge类的
static ArrayList[] map;
static class edge{
public int P,Q,D;
public edge(int p,int q,int d) {
P=p;
Q=q;
D=d;
}
}
private static void getResult() {
boolean [] vis=new boolean[n+1];
//进行两遍深搜 第一遍深搜搜索出最长路径的起点,第二次深搜走出最长路径
dfs(1,vis,0);
//布尔数组再次初始化
vis=new boolean[n+1];
dfs(point,vis,0);
int res=(11+10+Max)*Max/2;
System.out.println(res);
}
private static void dfs(int start,boolean[] vis,int dis ) {
vis[start]=true;
//枚举从start出发 可以到达的下一个城市
for(int i=0;i
edge temp=map[start].get(i);
if(!vis[temp.Q]) {//若这两个城市的高速公路没有访问过 则访问
dis+=temp.D;
if(dis>Max) {//若这条路的距离大于max 更新max
Max=dis;
point=temp.Q;//并且存储该城市
}
dfs(temp.Q,vis,dis);//沿着该点往下搜索
//需要恢复状态 因为在该条高速路走到黑之后进行下一条高速路的深搜
dis-= temp.D;
}
}
}
public static void main(String[] args) {
Scanner reader=new Scanner(System.in);
n=reader.nextInt();
//由n个城市 每个城市都有自己的map
map=new ArrayList[n+1];
for(int i=1;i<=n;i++)
//将每个城市的map都实例化为存储edge(高速路)对象的链表
map[i]=new ArrayList();
for(int j=1;j
int p=reader.nextInt();
int q=reader.nextInt();
int d=reader.nextInt();
map[p].add(new edge(p,q,d));
map[q].add(new edge(q,p,d));
}
reader.close();
getResult();
}
}
生命之树
在X森林里,上帝创建了生命之树。
他给每棵树的每个节点(叶子也称为一个节点)上,都标了一个整数,
代表这个点的和谐值。上帝要在这棵树内选出一个非空节点集S,
使得对于S中的任意两个点a,b,都存在一个点列 {a, v1, v2, ..., vk, b}
使得这个点列中的每个点都是S里面的元素,且序列中相邻两个点间有一条边相连。
在这个前提下,上帝要使得S中的点所对应的整数的和尽量大。
这个最大的和就是上帝给生命之树的评分。
经过atm的努力,他已经知道了上帝给每棵树上每个节点上的整数。
但是由于 atm 不擅长计算,他不知道怎样有效的求评分。
他需要你为他写一个程序来计算一棵树的分数。
「输入格式」
第一行一个整数 n 表示这棵树有 n 个节点。
第二行 n 个整数,依次表示每个节点的评分。
接下来 n-1 行,每行 2 个整数 u, v,表示存在一条 u 到 v 的边。
由于这是一棵树,所以是不存在环的。
「输出格式」
输出一行一个数,表示上帝给这棵树的分数。
「样例输入」
5
1 -2 -3 4 5
4 2
3 1
1 2
2 5
「样例输出」
8
「数据范围」
对于 30% 的数据,n <= 10
对于 100% 的数据,0 < n <= 10^5, 每个节点的评分的绝对值不超过 10^6 。
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 3000ms
这是一颗无向赋权图,要求是找一棵最大的连通块(生成树)。
package _2015;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
/**
* @author JohnnyLin
* @version Creation Time:2020年10月12日 下午11:04:40
*/
public class _t10生命之树 {
static int n;
static long ans;
//每个数组元素为列表
static List nodeEdge[];
static long[] value;
static void dfs(int son,int father) {
for(int i=0;i
int next=nodeEdge[son].get(i);
if(next!=father) {//相邻结点不是父结点
//子树最大连通块为负值则不要了 比如说3号结点的子树为负值
dfs(next, son);
value[son]+=Math.max(0,value[next]);
//System.out.println(value[next]);
}
}
//根结点可能是负值
ans=Math.max(ans,value[son]);
//System.out.println(ans);
}
public static void main(String[] args) {
Scanner reader=new Scanner(System.in);
n=reader.nextInt();
value=new long[n+1];
nodeEdge=new ArrayList[n+1];
for(int i=1;i<=n;i++) {
value[i]=reader.nextInt();
nodeEdge[i]=new ArrayList();
}
for(int i=1;i
int u=reader.nextInt();
int v=reader.nextInt();
nodeEdge[u].add(v);
nodeEdge[v].add(u);
}
dfs(1,-1);
System.out.println(ans);
}
}
另一种写法:
参考了网上代码的
package 第六届;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;
public class 生命树_网友版 {
static int[] nodeValue;//存放各个点的和谐值
static List[] point;//存放各个点的包含的邻接边
static int[] value;//存放以某个点为根节点情况下的最大和谐值和
static int ans=0;//记录最大结果
static void dfs(int son,int father) {
value[son]=nodeValue[son];//以son为根节点,一开始时其最大和谐值为它本身
for(int i=0;i
int next=point[son].get(i);
//当枚举的下一个结点是son先前经过的点father 跳过 防止绕圈
if(next==father)continue;//如果都continue掉说明这个点是叶子结点
dfs(next,son);
//回溯(子节点平行状态都走完没得走了或者说状态没得转移了才会回溯)得到父节点与子节点和
if(value[next]>0)//子节点的和谐值
value[son]+=value[next];
ans=Math.max(ans, value[son]);
}
}
public static void main(String[] args) {
Scanner reader=new Scanner(System.in);
int n=reader.nextInt();
nodeValue=new int [n+1];
value=new int[n+1];
point=new LinkedList[n+1];
for(int i=1;i<=n;i++) {
nodeValue[i]=reader.nextInt();
point[i]=new LinkedList();
}
for(int i=1;i
int x=reader.nextInt();
int y=reader.nextInt();
point[x].add(y);
point[y].add(x);
}
dfs(1,-1);//从0号结点开始搜索 假设它的父节点为-1可以为其他的无关量
System.err.println(ans);
}
}
注意:java里调用栈最多一万层,因此使用dfs最多可以通过n <= 10,30%的数据
蓝桥杯图专题相关教程
[蓝桥杯2017初赛]Excel地址(新型进制转换)(思路)
[蓝桥杯2017初赛]Excel地址(新型进制转换)(思路) 这道题自己想了好久没想明白,感觉有规律但就是不知道怎么去找。。。 看完别人的代码后,自己再想想,好有道理啊!!! #includeiostream#includestdio.h#includestring.h#includemath.h#includealgorithm
[蓝桥杯2017初赛]九宫幻方 (全排列匹配)
[蓝桥杯2017初赛]九宫幻方 (全排列匹配) 使用全排列函数,对1~9进行全排列,先验证一下是不是在题目所给的矩阵基础上填写的,然后在验证是否满足三阶幻方的要求。如果满足总计数器就+1,最后判断一下有几个满足条件的,按题目要求输出结果即可。 #includeio
蓝桥杯第九届省赛B组c/c++
蓝桥杯第九届省赛B组c/c++ 1.第几天 2000年的1月1日,是那一年的第1天。 那么,2000年的5月4日,是那一年的第几天? 注意:需要提交的是一个整数,不要填写任何多余内容。 解题思路: 直接算或者借助Excel表 ,2000年1月1日再加124天=2000年5月4日,所以是第12
蓝桥秘密冲刺计划(10.13)解码
蓝桥秘密冲刺计划(10.13)解码 定位:2020年第十一届蓝桥杯省赛C/C++ B组试题G(7月试题) 原题: 属性:字符读入+模拟 难度:破壳 独白: 这题我们采用边读取字符边模拟判断的策略来做,即用这样的while循环: while((ch=getchar())!='\n') 来解决问题。 读
[蓝桥杯2017初赛]9数算式(思路+水题)
[蓝桥杯2017初赛]9数算式(思路+水题) 说实话,这道题拖了我好久,我第一眼看题:这题稳了,不就是4,5全排列么。 但是做完后答案只有300多,然后再改,还是300多,就去看题解去了,才发现不一定非是4位数 5位数,还可能是3位数 6位数,2位数 7位数,1位数 8
[蓝桥杯2017初赛]纸牌三角形 (思路)
[蓝桥杯2017初赛]纸牌三角形 (思路) 真的是被秀了一脸!! 考虑旋转,镜像后,牌的顺序不变的情况下,有六种,所以最后结果除以6就行了。 #includeiostream#includestdio.h#includestring.h#includemath.h#includealgorithm#define ll long long#includeset
Python蓝桥杯练习基础题 杨辉三角形
Python蓝桥杯练习基础题 杨辉三角形 0 引言 马上就要进行蓝桥杯的省赛了,最近刷了一些蓝桥杯官网上的一些基础题,这里对杨辉三角形的python实现来进行一个思路的自我总结。 1 杨辉三角形 首先,直观认识一下杨辉三角;这里摘取一张百度百科上的杨辉三角形图
蓝桥-递增三元组-蓝桥
蓝桥-递增三元组-蓝桥 手动求解一下会发现,B数组是关键 若固定b = B[i] a中的可能的取值是:a0 ----- at小于等于b的元素下标(小于b的个数) c中的可能取值是:大于c的元素下标ct ---- cn 固定b后可以很容易的判断出a,c中的元素个数 这是一个分步策略,最后可