题目描述:

在一个无权图中,两个节点间的最短距离可以看成从一个节点出发到达另一个节点至少需要经过的边的个数。
同时,任意两个节点间的最短路径可能有多条,使得从一个节点出发可以有多条最短路径可以选择,并且沿着这些路径到达目标节点所经过的边的个数都是一样的。
但是在图中有那么一些特殊的节点,如果去除这些点,那么从某个初始节点到某个终止节点的最短路径长度要么变长,要么这两个节点变得不连通。这些点被称为最短路径上的关键点。
现给定一个无权图,以及起始节点和终止节点,要求输出该图上,这对节点间最短路径上的关键点数目。

输入:

输入包含多组测试数据,每组测试数据第一行为4个整数n(1<=n<=10000),m(1<=m<=100000),s(1<=s<=n),t(1<=t<=n)。分别代表该图中的节点个数n,边数量m,起始节点s,终止节点t。
接下去m行描述边的信息,每行两个整数a,b(1<=a,b<=n 且 a != b)。表示节点a和节点b之间有一条边。

输出:

对于每组测试数据,输出给定的这对节点间最短路径上的关键点数目。注意:若给定两个节点间不连通,则我们认为其关键点数目是0。

样例输入:
5 5 1 5
1 2
1 3
2 4
3 4
4 5
4 4 1 4
1 2
2 4
3 4
1 3
样例输出:
1
0 开始看到这个题觉得有些懵,
一开始用深度优先搜索来做,找到最短的那条路径,并且对路径上走过的每一个点进行计数
最后计数值和终点的计数值一致的点就是关键点
代码如下
 #include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <iostream>
#define POINT_CNT 10020 using namespace std; vector <int> adj[POINT_CNT];
int flag[POINT_CNT];
int cnt[POINT_CNT];
int path[POINT_CNT];
int tm[POINT_CNT];
int n, m, s, t;
int minStep; void dfs(int from, int step) {
if (step > tm[from]) {
return;
}
if (from != s && step == tm[from]) {
if (cnt[from] != ) {
for (int j = ; j <= minStep; j++) {
cnt[path[j]]++;
tm[path[j]] = j;
}
}
return;
}
if (from == t) {
if (step < minStep) {
memset(cnt, , sizeof(cnt));
for (int j = ; j <= step; j++) {
cnt[path[j]] = ;
tm[path[j]] = j;
}
minStep = step;
}
else if (step == minStep) {
for (int j = ; j <= step; j++) {
cnt[path[j]]++;
tm[path[j]] = j;
}
}
return;
}
int sz = adj[from].size();
for (int i = ; i < sz; i++) {
int p = adj[from][i];
if (flag[p] == ) {
flag[p] = ;
path[step + ] = p;
dfs(p,step+);
flag[p] = ;
}
}
} int main() {
//freopen("input.txt", "r", stdin);
//freopen("output.txt", "w", stdout); while (scanf("%d %d %d %d", &n, &m, &s, &t) != EOF) {
for (int i = ; i < POINT_CNT; i++) {
adj[i].clear();
}
while (m--) {
int a, b;
scanf("%d %d", &a, &b);
adj[a].push_back(b);
adj[b].push_back(a);
}
memset(flag, , sizeof(flag));
memset(cnt, , sizeof(cnt));
for (int i = ; i <= n; i++) {
tm[i] = POINT_CNT;
}
minStep = POINT_CNT; flag[s] = ;
path[] = s;
tm[s] = ;
dfs(s,);
int way = cnt[t];
int ans = ;
if (way == ) {
puts("");
continue;
}
for (int i = ; i <= n; i++) {
if (cnt[i] == way && i != t) {
ans++;
}
}
printf("%d\n", ans);
}
return ;
}

中间又做了一些剪枝的处理,但提交了好几次均超时。

无奈之下考虑广度优先搜索的思路

主要问题是如何找到关键点,此处我们需要遍历两次

第一次从源点s遍历到终点t,记录每一个经过点的层数

第二次从终点t遍历到起点s,只遍历那些层数比其小的。当队列为空时那个出队列的点就是关键点。

但一开始提交又是错误

代码如下

 #include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <iostream>
#include <queue> #define POINT_CNT 10020 using namespace std; vector <int> edge[POINT_CNT];
int flag[POINT_CNT];
int level[POINT_CNT];
int n, m, s, t;
int minStep;
queue <int> que; int main() {
//freopen("input.txt", "r", stdin);
//freopen("output.txt", "w", stdout); while (scanf("%d %d %d %d", &n, &m, &s, &t) != EOF) {
for (int i = ; i < POINT_CNT; i++) {
edge[i].clear();
}
while (m--) {
int a, b;
scanf("%d %d", &a, &b);
edge[a].push_back(b);
edge[b].push_back(a);
}
if (n == ) {
puts("");
continue;
}
memset(flag, , sizeof(flag));
memset(level, , sizeof(level));
while (!que.empty()) {
que.pop();
}
que.push(s);
flag[s] = ;
while (!que.empty()) {
int p = que.front(); que.pop();
int ps = edge[p].size();
int step = level[p]; if (p == t) {
break;
}
for (int i = ; i < ps; i++) {
int to = edge[p][i];
if (flag[to] == ) {
que.push(to);
level[to] = step + ;
flag[to] = ;
}
}
} while (!que.empty()) {
que.pop();
}
que.push(t);
int ans = ;
while (!que.empty()) {
int p = que.front(); que.pop();
int ps = edge[p].size();
int step = level[p];
if (p == s) {
break;
}
if (que.empty()) {
ans++;
}
for (int i = ; i < ps; i++) {
int to = edge[p][i];
if (level[to] < step) {
que.push(to);
}
}
}
printf("%d\n", ans-);
}
return ;
}

此时考虑特殊情况,如果只有一个点怎么办?如果有重边怎么办?

一是输出0,二是增加标记是否访问过

修改代码如下

 #include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <iostream>
#include <queue> #define POINT_CNT 10020 using namespace std; vector <int> edge[POINT_CNT];
int flag[POINT_CNT];
int level[POINT_CNT];
int n, m, s, t;
int minStep;
queue <int> que; int main() {
//freopen("input.txt", "r", stdin);
//freopen("output.txt", "w", stdout); while (scanf("%d %d %d %d", &n, &m, &s, &t) != EOF) {
for (int i = ; i < POINT_CNT; i++) {
edge[i].clear();
}
while (m--) {
int a, b;
scanf("%d %d", &a, &b);
edge[a].push_back(b);
edge[b].push_back(a);
}
if (n == ) {
puts("");
continue;
}
memset(flag, , sizeof(flag));
memset(level, , sizeof(level));
while (!que.empty()) {
que.pop();
}
que.push(s);
flag[s] = ;
while (!que.empty()) {
int p = que.front(); que.pop();
int ps = edge[p].size();
int step = level[p]; if (p == t) {
break;
}
for (int i = ; i < ps; i++) {
int to = edge[p][i];
if (flag[to] == ) {
que.push(to);
level[to] = step + ;
flag[to] = ;
}
}
} while (!que.empty()) {
que.pop();
}
que.push(t);
int ans = ;
memset(flag, , sizeof(flag));
flag[t] = ;
while (!que.empty()) {
int p = que.front(); que.pop();
int ps = edge[p].size();
int step = level[p];
if (p == s) {
break;
}
if (que.empty()) {
ans++;
}
for (int i = ; i < ps; i++) {
int to = edge[p][i];
if (level[to] < step && flag[to]==) {
que.push(to);
flag[to] = ;
}
}
}
printf("%d\n", ans-);
}
return ;
}

提交,还是错误。

。。。。。。。。。。

。。。。。。。。。。。。。。。。

。。。。。。。。。。。。。。。。。。

。。。。。。。。。。。。。。。。。。。。

。。。。。。。。。。。。。。。。。。。。。。

总是不过真是令人无奈

偶然发现自己初始化 level为0

那么有些和终点相连却并不和起点相连的点就会被访问,导致错误

比如

6就会被访问

再次修改如下

 #include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <iostream>
#include <queue> #define POINT_CNT 10020 using namespace std; vector <int> edge[POINT_CNT];
int flag[POINT_CNT];
int level[POINT_CNT];
int n, m, s, t;
int minStep;
queue <int> que; int main() {
//freopen("input.txt", "r", stdin);
//freopen("output.txt", "w", stdout); while (scanf("%d %d %d %d", &n, &m, &s, &t) != EOF) {
for (int i = ; i < POINT_CNT; i++) {
edge[i].clear();
}
while (m--) {
int a, b;
scanf("%d %d", &a, &b);
edge[a].push_back(b);
edge[b].push_back(a);
}
memset(flag, , sizeof(flag));
fill(level, level + POINT_CNT, POINT_CNT);
while (!que.empty()) {
que.pop();
}
que.push(s);
level[s] = ;
flag[s] = ;
while (!que.empty()) {
int p = que.front(); que.pop();
int ps = edge[p].size();
int step = level[p]; if (p == t) {
break;
}
for (int i = ; i < ps; i++) {
int to = edge[p][i];
if (flag[to] == ) {
que.push(to);
level[to] = step + ;
flag[to] = ;
}
}
} while (!que.empty()) {
que.pop();
}
que.push(t);
int ans = ;
memset(flag, , sizeof(flag));
flag[t] = ;
while (!que.empty()) {
int p = que.front(); que.pop();
int ps = edge[p].size();
int step = level[p];
if (p == s) {
break;
}
if (que.empty()) {
ans++;
}
for (int i = ; i < ps; i++) {
int to = edge[p][i];
if (level[to] < step && flag[to]==) {
que.push(to);
flag[to] = ;
}
}
}
printf("%d\n", max(ans-,));
}
return ;
}

提交终于通过了,

汗!!!!!!!!!!!!!!!!!!

九度oj 题目1495:关键点的更多相关文章

  1. 九度OJ 题目1384:二维数组中的查找

    /********************************* * 日期:2013-10-11 * 作者:SJF0115 * 题号: 九度OJ 题目1384:二维数组中的查找 * 来源:http ...

  2. hdu 1284 关于钱币兑换的一系列问题 九度oj 题目1408:吃豆机器人

    钱币兑换问题 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Sub ...

  3. 九度oj题目&amp;吉大考研11年机试题全解

    九度oj题目(吉大考研11年机试题全解) 吉大考研机试2011年题目: 题目一(jobdu1105:字符串的反码).    http://ac.jobdu.com/problem.php?pid=11 ...

  4. 九度oj 题目1007:奥运排序问题

    九度oj 题目1007:奥运排序问题   恢复 题目描述: 按要求,给国家进行排名. 输入:                        有多组数据. 第一行给出国家数N,要求排名的国家数M,国家号 ...

  5. 九度oj 题目1087:约数的个数

    题目链接:http://ac.jobdu.com/problem.php?pid=1087 题目描述: 输入n个整数,依次输出每个数的约数的个数 输入: 输入的第一行为N,即数组的个数(N<=1 ...

  6. 九度OJ题目1105:字符串的反码

    tips:scanf,cin输入字符串遇到空格就停止,所以想输入一行字符并保留最后的"\0"还是用gets()函数比较好,九度OJ真操蛋,true?没有这个关键字,还是用1吧,还是 ...

  7. 九度oj题目1009:二叉搜索树

    题目描述: 判断两序列是否为同一二叉搜索树序列 输入:                        开始一个数n,(1<=n<=20) 表示有n个需要判断,n= 0 的时候输入结束. 接 ...

  8. 九度oj题目1002:Grading

    //不是说C语言就是C++的子集么,为毛printf在九度OJ上不能通过编译,abs还不支持参数为整型的abs()重载 //C++比较正确的做法是#include<cmath.h>,cou ...

  9. 九度OJ题目1003:A+B

    while(cin>>str1>>str2)就行了,多简单,不得不吐槽,九度的OJ真奇葩 题目描述: 给定两个整数A和B,其表示形式是:从个位开始,每三位数用逗号", ...

随机推荐

  1. BUG-jQuery提交表单submit方法-TypeError: e[h] is not a function

    问题:button按钮设置id为submit后,表单jquery.submit()无法提交,报告异常TypeError: e[h] is not a function 源码: 解决:参考http:// ...

  2. es6 Reflect对象详解

    Reflect是ES6为操作对象而提供的新API,而这个API设计的目的只要有: 将Object对象的一些属于语言内部的方法放到Reflect对象上,从Reflect上能拿到语言内部的方法.如:Obj ...

  3. day 23 模块2

    1.namedtuple     命名元组  -> 类似创建一个类 from collections import namedtuple   # 类 p = namedtuple("P ...

  4. Linux字符设备驱动--No.3

    字符驱动(按键)初始化函数分析: int charDrvInit(void) { devNum = MKDEV(reg_major, reg_minor); printk(KERN_EMERG&quo ...

  5. springboot之websocket

    一.WebSocket协议是基于TCP的一种新的网络协议.它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端. 二.长久以来, 创建实现客户端和用户端之间双工 ...

  6. netty之编解码

    1.netty的编码和解码,在数据传输的时候,考虑数据安全,数据完整性都是很有必要的.这里主要是介绍netty3和netty5的编解码方式.其实从StringEncoder和StringDecoder ...

  7. Thymeleaf 模板引擎用法

    学习.改良.极致 博客园 首页 新随笔 联系 管理 订阅 随笔- 31  文章- 0  评论- 50  Thymeleaf 常用属性   文章主目录 th:action th:each th:fiel ...

  8. FreeRTOS的信号量和互斥量

    1. 理解如下,言简意赅的说,信号量解决同步,互斥量解决竞争. 信号量用于同步,主要任务间和中断间同步:互斥量用于互锁,用于保护同时只能有一个任务访问的资源,为资源上一把锁. 互斥量具有优先级继承,信 ...

  9. nodejs 事件机制

    node 事件机制   一 三种定时器 NodeJS中有三种类型的定时器:超时时间.时间间隔.即时定时器 1.超时时间:setTimeout(callback,delayMilliSeconds,[a ...

  10. hive 中的float和double

    表employees中字段 taxes(税率)用类型float存储 hive> select name, salary, taxes from employees where taxes  &g ...