蓝桥杯-专题-简单图论(大臣旅费&网络寻路)

很久没写题了,最近就刷刷蓝桥杯,本来图论写的少,就接触过初级的最短路,现在就是练练手
1.大臣旅费
题目

很久以前,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最多花费的路费是多少。
样例输入:
5
1 2 2
1 3 1
2 4 5
2 5 4
样例输出:
135
样例说明:
大臣J从城市4到城市5要花费135的路费。
资源约定:
峰值内存消耗 < 64M
CPU消耗 < 5000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意: main函数需要返回0
注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
注意: 所有依赖的函数必须明确地在源文件中 #include , 不能通过工程设置而省略常用头文件。
提交时,注意选择所期望的编译器类型。

思路

我觉得的呀!好像挺容易想到找两个距离最长的点就行,就是这两个点叫“树的直径”。然后直径的简单求解就是:从任意一点出发,进行一遍搜索(BFS/DFS)找直径的一个端点;在从这个端点出发,再进行一遍搜索(DFS/BFS)找直径的另一个端点。直径求出来就行了。
数据结构,我就用的普通的邻接表和邻接矩阵。 链式前向星不太会呀。。。

代码

#include<bits/stdc++.h>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))//不能mem(double)
#define ll long long
const double eps=3e-8;
const int mod=10;
const int maxn=10005;//最好数量范围大一点;1e4+5  &&  long long
vector<int>ed[maxn];//存储每个点的邻接点
ll edge[maxn][maxn];//存储每个边值
ll dis[maxn];//存储bfs中从node点开始的路径长
ll sum=0;
int bfs(int node){
    mem(dis,-1);
    queue<int>que;
    que.push(node);
    int ans=node;
    dis[node]=0;
    while(!que.empty()){
        int now=que.front();
        que.pop();
        for(int i=0;i<ed[now].size();i++){
            int temp=ed[now][i];
            if(dis[temp]<0){
                dis[temp]=dis[now]+edge[now][temp];
                if(dis[temp]>sum){
                    ans=temp;
                    sum=dis[temp];
                }
                que.push(temp);
            }
        }
    }
    return ans;
}
int main(){
    freopen("in4.txt","r",stdin);
    int n,a,b;
    ll c;
    scanf("%d",&n);
    for(int i=1;i<n;i++){
        scanf("%d%d%lld",&a,&b,&c);
        ed[a].push_back(b);
        ed[b].push_back(a);
        edge[a][b]=c;
        edge[b][a]=c;
    }
    int starta=1;
    int endnode,startnode;
    sum=0;
    endnode=bfs(starta);
    sum=0;
    startnode=bfs(endnode);
    ///cout<<endnode<<" "<<startnode<<" "<<sum<<endl;
    double ans=sum*(sum+1.0)/2+10.0*sum;
    printf("%.0f\n",ans);
}

2.网络寻路
题目
图一在这里插入图片描述

X 国的一个网络使用若干条线路连接若干个节点。节点间的通信是双向的。某重要数据包,为了安全起见,必须恰好被转发两次到达目的地。该包可能在任意一个节点产生,我们需要知道该网络中一共有多少种不同的转发路径。
源地址和目标地址可以相同,但中间节点必须不同。
如图1所示的网络。
1 -> 2 -> 3 -> 1 是允许的
1 -> 2 -> 1-> 2 或者 1->2->3->2 都是非法的。
输入数据的第一行为两个整数N M,分别表示节点个数和连接线路的条数(1<=N<=10000; 0<=M<=100000)。
接下去有M行,每行为两个整数 u 和 v,表示节点u 和 v 联通(1<=u,v<=N , u!=v)。
输入数据保证任意两点最多只有一条边连接,并且没有自己连自己的边,即不存在重边和自环。
输出一个整数,表示满足要求的路径条数。
例如:
用户输入:
3 3
1 2
2 3
1 3
则程序应该输出:
6
再例如:
用户输入:
4 4
1 2
2 3
3 1
1 4
则程序应该输出:
10
资源约定:
峰值内存消耗 < 64M
CPU消耗 < 1000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意: main函数需要返回0
注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
注意: 所有依赖的函数必须明确地在源文件中 #include , 不能通过工程设置而省略常用头文件。
提交时,注意选择所期望的编译器类型(千万不要混淆c和cpp)。

思路

这里是引用答案。
[题意]
给你一个图,N个节点,M条无向边.
问你从任意节点走3步后,到达另外一个节点的走法数有多少种(开始位置和结束位置可能相同,也就是走三元环)
[解析]
走3步,也就是走了三条边.
那么我们可以枚举三条边的中间那条边为edge(u,v).
那么: (节点u连出去的边数-1)(节点v连出去的边数-1); 就是第二步走中转边edge(u,v)的方法数.
所以只要枚举每条边作为中转边的方法数之和为ans即可.
因为边是双向的,所以最后答案ans
2即为最终结果.
只要想到通过中转边来统计,此题就是不难了.

这大概就是枚举的奥妙!~ 我本来从两头找中间,但是好像时间复杂度不太像,于是后来改成从中间找两头了。

代码

#include<bits/stdc++.h>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))//不能mem(double)
#define ll long long
const double eps=3e-8;
const int mod=10;
const int maxn=10005;//最好数量范围大一点;1e4+5  &&  long long
vector<int>ed[maxn];//存储每个点的邻接点
ll sum=0;
struct node{
    ll from,to;
    node(int x,int y):from(x),to(y){}
    node(){}
};
vector<node>edges;
int main(){
    freopen("in5.txt","r",stdin);
    ll n,m,a,b;
    scanf("%lld%lld",&n,&m);
    for(int i=0;i<m;i++){
        scanf("%lld%lld",&a,&b);
        ed[a].push_back(b);
        ed[b].push_back(a);
        edges.push_back(node(a,b));
    }
    ll ans=0;
    for(int i=0;i<edges.size();i++){
        int from=edges[i].from;
        int to=edges[i].to;
        ans+=(ed[from].size()-1)*(ed[to].size()-1);
    }
    cout<<ans*2LL<<endl;
    return 0;
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值