题目描述:

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

输入:

输入包含多组测试数据,每组测试数据第一行为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. hadoop生态搭建(3节点)-15.Nginx_Keepalived_Tomcat配置

    # Nginx+Tomcat搭建高可用服务器名称 预装软件 IP地址Nginx服务器 Nginx1 192.168.6.131Nginx服务器 Nginx2 192.168.6.132 # ===== ...

  2. python应用:爬虫框架Scrapy系统学习第二篇——windows下安装scrapy

    windows下安装scrapy 依次执行下列操作: pip install wheel pip install lxml pip install PyOpenssl 安装Microsoft visu ...

  3. 使用ansible安装lnmp

    主机互信 生成密钥对,并将公钥发送给其他需要操作的主机 ssh-keygen -t rsa cd /root/.ssh ssh-copy-id -i id_rsa.pub root@192.168.1 ...

  4. Django的安装创建与连接数据库

    HTTP协议简介 HTTP是一个客户端终端(用户)和服务器端(网站)请求和应答的标准(TCP).通过使用网 页浏览器.网络爬虫或者其它的工具,客户端发起一个HTTP请求到服务器上指定端口(默认端 口为 ...

  5. python新手学习之文件读写之修改

    文件除r.w.a方式打开外,还可以有多种组合方式如r+ w+ a+等多种方式 1.r+ 读写模式介绍,开始读是从一行开始读,写永远从最后开始写(类似于追加) # f = open("test ...

  6. 1010 一元多项式求导 (25 分) C语言

    设计函数求一元多项式的导数.(注:x​n​​(n为整数)的一阶导数为nx​n−1​​.) 输入格式: 以指数递降方式输入多项式非零项系数和指数(绝对值均为不超过 1000 的整数).数字间以空格分隔. ...

  7. LeetCode初级算法的Python实现--动态规划

    动态规划的本质是递归:所以做题之前一定要会递归:递归式就是状态转移方程:这里将会介绍使用动态规划做题的思维方式. 统一的做题步骤: 1.用递归的方式写出代码:(此方法写的代码在leetcode中一定会 ...

  8. CF480Div2掉分记

    rating 1900+参加只有Div2的比赛也记rating了.还以为yyc报名没打会惨惨,原来不交题好像就不算参加.. 本来太晚了不想打,不过有Sinogi大佬带我还是打一打吧,apio之前练练手 ...

  9. 每天看一片代码系列(一):stream.js

    简介 stream.js是一个小型的js库,用于处理stream相关的操作.这里的stream是指一种数据结构,它像数组一样,可以放置多个类型的数据,但是并不限制长度,甚至可以达到无限长.可以对该数据 ...

  10. ubuntu16安装过程中的硬盘分区

    硬盘分区主要是三个区,如下图,分别是/,swap交换空间,/home,其中第一个分区/是主分区,建议至少8GB,要不ubuntu安装的时候放不下.