一、题目
资源限制
时间限制:1.0s 内存限制:256.0MB
问题描述
X 国的一个网络使用若干条线路连接若干个节点。节点间的通信是双向的。某重要数据包,为了安全起见,必须恰好被转发两次到达目的地。该包可能在任意一个节点产生,我们需要知道该网络中一共有多少种不同的转发路径。
源地址和目标地址可以相同,但中间节点必须不同。
如下图所示的网络。
1 -> 2 -> 3 -> 1 是允许的
1 -> 2 -> 1 -> 2 或者 1 -> 2 -> 3 -> 2 都是非法的。
输入格式
输入数据的第一行为两个整数N M,分别表示节点个数和连接线路的条数(1<=N<=10000; 0<=M<=100000)。
接下去有M行,每行为两个整数 u 和 v,表示节点u 和 v 联通(1<=u,v<=N , u!=v)。
输入数据保证任意两点最多只有一条边连接,并且没有自己连自己的边,即不存在重边和自环。
输出格式
输出一个整数,表示满足要求的路径条数。
样例输入1
3 3
1 2
2 3
1 3
样例输出1
6
样例输入2
4 4
1 2
2 3
3 1
1 4
样例输出2
10
二、解题思路
我的想法是通过深度优先遍历去找这个点所有有可能的线路,因为题目要求是4个点,所以第四个点有可能是起点,形成一个环,这里我在深度遍历里加了个条件,使得在点数为3的时候进行判断,如果第三个点跟起点有边的话,计数就加一。循环深度遍历所有点,得到最终的计数结果。
三、代码实现
这个代码,得了四十分,后两个超时,最后一个运行错误,我在本地跑了一下,结果是对的。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;
public class Main {
static int n,m,count=0;
static int [][]arr;
static int []flag;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n=sc.nextInt();
m=sc.nextInt();
arr=new int[n+1][n+1];
flag=new int[n+1];
for(int i=1;i<=m;i++) {
int u=sc.nextInt();
int v=sc.nextInt();
arr[u][v]=arr[v][u]=1;
}
for(int i=1;i<=n;i++) {
DFS(i,i,1);
Arrays.fill(flag,0);
// System.out.println("次数为"+i+"时,count为:"+count);
// System.out.println();
}
System.out.println(count);
}
private static void DFS(int pot,int st,int bu) {
// System.out.println("进入点:"+st);
if(bu==4) {
count++;
}
if(bu>4) {
return;
}
// System.out.println("此时点:"+st+"的count为:"+count);
// System.out.println("此时步数为:"+bu);
flag[st]=1;
for(int i=1;i<=n;i++) {
if(arr[st][i]==1 && flag[i]==0) {
DFS(pot,i,bu+1);
flag[i]=0;
}
if(bu==3 && i==n) {//i==n使这个判断条件只会出现一次
if(arr[st][pot]==1) {
count++;
}
}
}
return;
}
}
大臣的旅费的经验让我萌发了把二维数组改成ArrayList做,还是四十分,但最后一个变成了运行超时,看来ArrayList运行的时间不会缩短多少,只是让内存用少点(自己的感觉)
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;
public class Main {
static int n,m,count=0;
static ArrayList []arr;
static int []flag;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n=sc.nextInt();
m=sc.nextInt();
arr=new ArrayList[n];
flag=new int[n+1];
for(int i=0;i<n;i++) {
arr[i]=new ArrayList();
}
for(int i=0;i<m;i++) {
int u=sc.nextInt();
int v=sc.nextInt();
arr[u-1].add(v-1);
arr[v-1].add(u-1);
}
for(int i=0;i<n;i++) {
DFS(i,i,1);
Arrays.fill(flag,0);
// System.out.println("次数为"+i+"时,count为:"+count);
// System.out.println();
}
System.out.println(count);
}
private static void DFS(int pot,int st,int bu) {
// System.out.println("进入点:"+(st+1));
if(bu==4) {
count++;
}
if(bu>4) {
return;
}
// System.out.println("此时点:"+(st+1)+"的count为:"+count);
// System.out.println("此时步数为:"+bu);
int len=arr[st].size();
flag[st]=1;
for(int i=0;i<len;i++) {
int j=(int) arr[st].get(i);
if(flag[j]==0) {
DFS(pot,j,bu+1);
flag[j]=0;
}
if(bu==3 && i==len-1) {
if(arr[st].contains(pot)) {
count++;
}
}
}
return;
}
}
把scanner输入改为buffer输入,第三个测试点也正确了。接着把深度遍历里的条件调整了一下,去掉过多的遍历。一百分的代码如下:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;
public class Main {
static int n,m,count=0;
static ArrayList<Integer> []arr=new ArrayList[10001];
static boolean []flag=new boolean[10001];
public static void main(String[] args) throws IOException {
BufferedReader bfr = new BufferedReader(
new InputStreamReader(System.in));
String c[]=bfr.readLine().split(" ");
int n = Integer.parseInt(c[0]);
int m = Integer.parseInt(c[1]);
int u,v;
for(int i=0;i<n;i++) {
arr[i]=new ArrayList();
}
for(int i=0;i<m;i++) {
c=bfr.readLine().split(" ");
u=Integer.parseInt(c[0]);
v=Integer.parseInt(c[1]);
arr[u-1].add(v-1);
arr[v-1].add(u-1);
}
for(int i=0;i<n;i++) {
DFS(i,i,1);
Arrays.fill(flag,false);
}
System.out.println(count);
}
private static void DFS(int pot,int st,int bu) {
int len=arr[st].size();
flag[st]=true;
for(int i=0;i<len;i++) {
int j=arr[st].get(i);
/*步数为3时,得到的节点如果跟起点相同说明成环,或者得到的节点还没走过就直接加一*/
if(bu==3 && ((j==pot) || flag[j]==false)) {
count++;
continue;//提前结束循环
}
if(flag[j]==false) {
DFS(pot,j,bu+1);
flag[j]=false;
}
}
return;
}
}
个人总结
- ArrayList比传统二维数组省内存
- 使用buffer代替scanner输入,速度明显提升
- 注意break和continue语句的灵活运用,可以大大的优化代码速度