九度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,其表示形式是:从个位开始,每三位数用逗号", ...
随机推荐
- SST-超级简单任务调度器结构分析
SST(Super Simple Task) 是一个基于任务优先级.抢占式.事件驱动.RTC.单堆栈的超级简单任务调度器,它基于Rober Ward一篇论文的思想,Miro Samek用C重新编程实现 ...
- C# set 跟 get
可以在类里面 private string name; public string Name { get { return name; } set { name = value; } }
- 汇编中resb这样的指令是什么意思?
转载下来,方便以后查看 原作网址:http://blog.csdn.net/m1j2t3/article/details/5681657 汇编中resb这样的指令是什么意思? 还有我在汇编程序中看到这 ...
- NoSQL入门第三天——Redis配置文件与持久化
一.解析Redis配置文件redis.conf (Linux下配置多于编码) 1.它在哪 由于我是在root的家目录下载安装的,默认的安装位置就是: conf就在这里: 根据经验,出厂的conf永远不 ...
- .NET : 开发ActiveX控件(转载)
我估计有些朋友不清楚ActiveX控件,但这篇博客不是来解释这些概念的.如果你对ActiveX的概念不清楚,请参考这里: http://baike.baidu.com/view/28141.htm 这 ...
- Dinic算法最大流入门
例题传送门 Dinic算法是网络流最大流的优化算法之一,每一步对原图进行分层,然后用DFS求增广路.时间复杂度是O(n^2*m),Dinic算法最多被分为n个阶段,每个阶段包括建层次网络和寻找增广路两 ...
- 浅谈Hash在多个字符串匹配类型问题中的应用
在生活中们有时会遇到一些有关字符串匹配的问题. 这时打暴力往往显得很愚蠢,效率低下. 所以就需要一些算法和数据结构来提高效率. Hash Hash,一般翻译做“散列”,也有直接音译为“哈希”的,就是把 ...
- 北京Uber优步司机奖励政策(3月21日)
滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...
- hive整合sentry,impala,hue之后权限管理操作
7.Hive授权参考(开启sentry之后,对用户授权用不了,只能针对用户组,grant role testrole to user xxxxxxx; ) 7.1:角色创建和删除 create rol ...
- serv-u自动停止的解决方法
在主界面serv-u管理控制台-主页--管理服务器----服务器详细信息下,点击“创建,修改并删除服务器事件”找到“事件”右击空白处---“添加”然后如下图所示填写: 点击“保存”就好了,而且我自己也 ...