CCF CSP 高速公路 JAVA 201509-4 100分
来源:互联网 发布:网络销售具体做什么 编辑:程序博客网 时间:2024/05/16 19:11
题目放下面了
两种解法:
1.tarjan算法 专门用来求这种强连通分量,可以解到N<10000,M<100000的数据(1S内)
2.禁忌的解法=。= (感觉和Kosaraju有点像?) 但只能解到N<1000,M<10000的数据(效率差了10倍不止),但优点在于理解简单,容易操作
先说第一种解法:
tarjan算法介绍:https://wenku.baidu.com/view/112c64096c85ec3a87c2c527.html?re=view
——如果看不懂可以看进一步解释:http://www.cnblogs.com/dqsBK/p/5350257.html
——再看不懂就来看看实际运作过程:http://blog.csdn.net/mystery_guest/article/details/51912713
相信到这里一定已经懂了
引用:
dfn数组: 意思就是在dfs的过程中,当前的这个节点是第几个被访问的
low数组: 有些并查集的意思,就是在dfs的过程中,标记如果当前节点是极大强联通子图的话,他的根节点的标号就是对应的low值:
如果下一个要遍历的节点在栈中,那么就要把当前节点的low值设置成 下一个节点(在栈中)的dfn值,dfn值是什么呢?就是记录这个节点是第几个被遍历到的。
如果下一个要遍历的节点不再栈中,那么就要把当前节点的low值设置成下一个节点和当前节点的low值中最小的那个。
看懂了过程,只要自己心里多模拟几遍,差不多就掌握这个算法啦:
实际代码:(JAVA版)——JAVA比C++慢一点,C++100分,JAVA同样是90分,不公平TOT
package csp2015_09_4;import java.util.ArrayList;import java.util.List;import java.util.Scanner;import java.util.Stack;public class Main{static List<Integer> list[];static boolean visited[];static int DFN[];static int LOW[];static Stack<Integer> s ;static int index;static int ans = 0;public static void main(String [] args){Scanner sc = new Scanner(System.in);int n = sc.nextInt();int m = sc.nextInt();list = new ArrayList[n+1];for (int i = 0; i <= n; i++) {list[i] = new ArrayList<Integer>();}int a,b;for (int i = 0; i < m; i++) {a = sc.nextInt();b = sc.nextInt();list[a].add(b);}s = new Stack<Integer>();visited = new boolean[n+1];DFN = new int[n+1];LOW = new int[n+1];index = 0;for (int i = 1; i <= n; i++) {if(!visited[i])tarjan(i);}//tarjan(1);System.out.println(ans);}private static void tarjan(int u) {visited[u] = true;DFN[u] = LOW[u] = ++index;s.push(u);int v;for (int i = 0; i < list[u].size(); i++) {v = list[u].get(i);if(!visited[v]){tarjan(v);LOW[u] = Math.min(LOW[u], LOW[v]);}else if(s.contains(v)){LOW[u] = Math.min(LOW[u],DFN[v]);}}if(DFN[u] == LOW[u]){int count = 0;do{v = s.pop();count++;}while(u!=v);if(count>0){ans += count*(count-1)/2;}//自己好好想想为什么!找了半天BUG=。=//if(count>1){//ans += count;//}}}}
简单的说就是深搜一遍,然后得到一个N*N的数组,(vis[i][j]意思是 点i可以连通到点j)(根据这样的定义,我们只要确定点i可以连通到点j时,点j也可以连通到点i,即是vis[i][j] == vis[j][i] == true 那么就说明这两个点强连通,满足题意,ans++)
因为只深搜了一遍,所以只有简单的点i和相邻的各个点j的关系,我们希望当点j和点k相连时,有vis[i][k]连通。
怎么实现呢,最简单的方法就是对每个点都再进行一次深搜,确定起始点s,每深搜到一个点k都意味着s和k直接或间接相连,令vis[s][k] == true即可。
// 由于这样其实是进行了多次的DFS搜索,所以效率肯定是比不上方法一的O(V+E),但也能跑出N<1000,M<10000的数据,当数据量不大的时候这样用用也无妨,至少也算一种思路吧。
代码如下:
1.递归法 2.队列法
注意递归法在数据量很大的时候会爆栈,抛出异常,然后OJ系统就会提示你:运行错误,60分
队列的话就是:超时,60分
至于哪个更快点,不知道,应该也差不了太多,也没有必要深究了。
package csp2015_09_4;/** * * 用队列 虽然不会爆栈了 但依然超时了=。= 而且空间上使用会大一点 至于哪个更快? 反正两个都超时了 * 重要:经测试:当N<=1000,M<=10000时,可以通过全部数据 * 大于这个数据就别用这个方法了,会超时 * */import java.util.ArrayList;import java.util.LinkedList;import java.util.List;import java.util.Queue;import java.util.Scanner;public class Main_Final{static List<Integer> list[];static boolean vis[][];static boolean ns[];public static void main(String [] args){Scanner sc = new Scanner(System.in);int n = sc.nextInt();int m = sc.nextInt();list = new ArrayList[n+1];for (int i = 0; i <= n; i++) {list[i] = new ArrayList<Integer>();}vis = new boolean[n+1][n+1];int a,b;for (int i = 0; i < m; i++) {a = sc.nextInt();b = sc.nextInt();list[a].add(b);}//递归法://dfs(1);//for (int i = 1; i <= n; i++) {//ns = new boolean[n+1];//ns[i] = true;//dfs2(i,i);//}//队列法: Queue<Integer> q = new LinkedList(); for (int t = 1; t <= n; t++) { q.add(t); ns = new boolean[n+1]; while(!q.isEmpty()){ int temp = q.poll(); if(!ns[temp]){ for (int i = 0; i < list[temp].size(); i++) { int next = list[temp].get(i); q.add(next);} ns[temp] = true; vis[t][temp] = true; } }}int ans = 0;for (int i = 1; i < vis.length; i++) {for (int j = 1; j < vis[i].length; j++) {//System.out.print(vis[i][j]+" ");if(i!=j &&vis[i][j]&&vis[j][i]) {//System.out.println(i+" "+j);ans++;}}//System.out.println();}System.out.println(ans/2);}private static void dfs2(int s, int i) {for (int j = 0; j < list[i].size(); j++) {int to = list[i].get(j);if(!ns[to]){ns[to] = true;vis[s][to] = true;dfs2(s,to);}}}private static void dfs(int i) {for (int j = 0; j < list[i].size(); j++) {if(!vis[i][list[i].get(j)]){int to = list[i].get(j);vis[i][to] = true;dfs(to);}}}}
题目如下:
现在,大臣们帮国王拟了一个修高速公路的计划。看了计划后,国王发现,有些城市之间可以通过高速公路直接(不经过其他城市)或间接(经过一个或多个其他城市)到达,而有的却不能。如果城市A可以通过高速公路到达城市B,而且城市B也可以通过高速公路到达城市A,则这两个城市被称为便利城市对。
国王想知道,在大臣们给他的计划中,有多少个便利城市对。
接下来m行,每行两个整数a, b,表示城市a有一条单向的高速公路连向城市b。
1 2
2 3
3 4
4 2
3 5
城市间的连接如图所示。有3个便利城市对,它们分别是(2, 3), (2, 4), (3, 4),请注意(2, 3)和(3, 2)看成同一个便利城市对。
前60%的评测用例满足1 ≤ n ≤ 1000, 1 ≤ m ≤ 10000;
所有评测用例满足1 ≤ n ≤ 10000, 1 ≤ m ≤ 100000。
- CCF CSP 高速公路 JAVA 201509-4 100分
- CCF CSP 201509-1 数列分段(Java-100分)
- CCF CSP 201509-2 日期计算(Java-100分)
- CCF-CSP 游戏 JAVA 201604-4 100分
- CCF-CSP 通信网络 JAVA 201709-4 100分
- CCF CSP 201703-1 分蛋糕(Java-100分)
- ccf 201509-4 高速公路
- CCF 201509-4 高速公路
- CCF 201509-4 高速公路
- 201509-4 高速公路 ccf
- ccf 201509-4高速公路
- CCF CSP 201703-2 学生排队(Java-100分)
- CCF CSP 201612-1 中间数(Java-100分)
- CCF CSP 201612-2 工资计算(Java-100分)
- CCF CSP 201612-3 权限查询(Java-100分)
- CCF CSP 201609-1 最大波动(Java-100分)
- CCF CSP 201609-2 火车购票(Java-100分)
- CCF CSP 201604-2 俄罗斯方块(Java-100分)
- IE浏览器的bug
- C# pdf 缩放纸型-iTextSharp
- HTK 安装、编译以及测试——Ubuntu 16.04
- 高软实验五报告
- 机器学习理解之SVM原理详解
- CCF CSP 高速公路 JAVA 201509-4 100分
- AngularJs路由与添加用户(2)
- "[用户名] is not in the sudoers file"(已解决)
- crontab关于 >/dev/null 2>&1输出重定向问题
- 在哈尔滨工作的第二年
- ActiveMQ(六):spring+ActiveMQ+线程池实现简单的分布式,多线程,多任务的异步任务处理系统
- HTML网页中table居中和表格内容居中
- Unable to open debugger port (127.0.0.1:43657)
- Linux进程间通信--消息队列