九度oj 题目1495:关键点
- 题目描述:
-
在一个无权图中,两个节点间的最短距离可以看成从一个节点出发到达另一个节点至少需要经过的边的个数。
同时,任意两个节点间的最短路径可能有多条,使得从一个节点出发可以有多条最短路径可以选择,并且沿着这些路径到达目标节点所经过的边的个数都是一样的。
但是在图中有那么一些特殊的节点,如果去除这些点,那么从某个初始节点到某个终止节点的最短路径长度要么变长,要么这两个节点变得不连通。这些点被称为最短路径上的关键点。
现给定一个无权图,以及起始节点和终止节点,要求输出该图上,这对节点间最短路径上的关键点数目。
- 输入:
-
输入包含多组测试数据,每组测试数据第一行为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:关键点的更多相关文章
- 九度OJ 题目1384:二维数组中的查找
/********************************* * 日期:2013-10-11 * 作者:SJF0115 * 题号: 九度OJ 题目1384:二维数组中的查找 * 来源:http ...
- hdu 1284 关于钱币兑换的一系列问题 九度oj 题目1408:吃豆机器人
钱币兑换问题 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Sub ...
- 九度oj题目&吉大考研11年机试题全解
九度oj题目(吉大考研11年机试题全解) 吉大考研机试2011年题目: 题目一(jobdu1105:字符串的反码). http://ac.jobdu.com/problem.php?pid=11 ...
- 九度oj 题目1007:奥运排序问题
九度oj 题目1007:奥运排序问题 恢复 题目描述: 按要求,给国家进行排名. 输入: 有多组数据. 第一行给出国家数N,要求排名的国家数M,国家号 ...
- 九度oj 题目1087:约数的个数
题目链接:http://ac.jobdu.com/problem.php?pid=1087 题目描述: 输入n个整数,依次输出每个数的约数的个数 输入: 输入的第一行为N,即数组的个数(N<=1 ...
- 九度OJ题目1105:字符串的反码
tips:scanf,cin输入字符串遇到空格就停止,所以想输入一行字符并保留最后的"\0"还是用gets()函数比较好,九度OJ真操蛋,true?没有这个关键字,还是用1吧,还是 ...
- 九度oj题目1009:二叉搜索树
题目描述: 判断两序列是否为同一二叉搜索树序列 输入: 开始一个数n,(1<=n<=20) 表示有n个需要判断,n= 0 的时候输入结束. 接 ...
- 九度oj题目1002:Grading
//不是说C语言就是C++的子集么,为毛printf在九度OJ上不能通过编译,abs还不支持参数为整型的abs()重载 //C++比较正确的做法是#include<cmath.h>,cou ...
- 九度OJ题目1003:A+B
while(cin>>str1>>str2)就行了,多简单,不得不吐槽,九度的OJ真奇葩 题目描述: 给定两个整数A和B,其表示形式是:从个位开始,每三位数用逗号", ...
随机推荐
- 【Spark】源码分析之SparkContext
一.概述 SaprkContext非常重要,是Spark提交任务到集群的入口 SparkContext中没有main方法,在SparkContext主构造器中,主要做一下四件事情: 1. 调用crea ...
- LCD驱动程序编写
学习目标:编写LCD驱动程序,熟悉根据芯片手册分析时序图,配置寄存器,并测试LCD程序. 一.LCD驱动程序编写 步骤: 1)分配fb_info结构体 2)设置fb_info结构体 a. 固定参数 b ...
- U盘kali系统安装
正言: 起初先百度了一下U盘安装Kali的资料,有很多版本和方法,当然还是以百度经验为例开始操作https://jingyan.baidu.com/article/cdddd41ca1027e53 ...
- Shell--cut用法
cut是以每一行为一个处理对象的,这种机制和sed一样. cut接受三个定位方法: 1)byte: -b 2)characters: -c 3)fields: -d eg:提取第3,4,5,9的字节: ...
- (数据科学学习手札43)Plotly基础内容介绍
一.简介 Plotly是一个非常著名且强大的开源数据可视化框架,它通过构建基于浏览器显示的web形式的可交互图表来展示信息,可创建多达数十种精美的图表和地图,本文就将以jupyter notebook ...
- 成都Uber优步司机奖励政策(1月26日)
滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...
- 水灾 1000MS 64MB (广搜)
水灾(sliker.cpp/c/pas) 1000MS 64MB 大雨应经下了几天雨,却还是没有停的样子.土豪CCY刚从外地赚完1e元回来,知道不久除了自己别墅,其他的地方都将会被洪水淹没. CCY ...
- 问题:MongoDB C# driver异常:Truncation resulted in data loss
问题描述: 原因分析: MongoDB C#驱动在读取数据记录遇到数值类型字段时,如果没有设置允许截断,将抛出TruncationException. 解决方法: [BsonRepresentatio ...
- android 学习四 ContentProvider
1.系统自带的许多数据(联系人,本地信息等)保存在sqllite数据库,然后封装成许多ContentProvider来供其他程序访问. 2.对sqllite数据库的操作,可以在命令行通过adb工具登录 ...
- redmine本地安装部署
1.railsinstaller-3.2.0.exe 下载地址 http://railsinstaller.org/en 安装railsinstaller 一直点next就可以了,安装完成之后C盘会 ...