描述
题解
很简单的一道题,但是前天上 java 课老师让我们练题,给大家出了一道这题,一看数据就知道这个一定是 dfs+邻接表,然而我没有跟着专业学 java,倒是主攻的 C/C++,所以写 C/C++ 版也就分分钟的事,可是写 java 版的我好心塞,java没有指针,猛一下不知道链表怎么写,我也不会用 java 的类库,java 满共学了十天不到,还是一年前自学的,倒是可以看懂,自己写却问题百出,由于能力有限,课上花了二十几分钟写了一个邻接矩阵+dfs 的解法,只能过部分数据,可是邻接表我就不知道具体语法怎么实现了,折腾了好久才解决,不太理解 java 的有些错误提示,太头疼了,让我给同学写题解,赶鸭子上架……
这里提供三种代码,一种是为了方便大家学习 dfs 的 java 写的邻接矩阵+dfs 的解法(代码One),只能过部分数据,但是对于学习 dfs 已经足够了,一种 C/C++写的邻接表+dfs 的解法(代码 Two),提交 AC 了,还有一种 java 写的邻接表+dfs 的解法(代码 Three),由于官网现在在维护中,所以就不提交了,本地测试没问题。
代码
One:
package javatest;
import java.util.Scanner;
public class NodeTongXin
{
static int N, M;
static int graph[][] = new int[1000][1000]; // 邻接矩阵
static int vis[][] = new int[1000][1000]; // 标识矩阵,标识(i,j)是否用过
static long cnt = 0; // 累加器
private static Scanner input;
static void dfs(int st, int step) // 起点,剩余边数
{
if (step == 0) // 剩余0条边后 GG
{
cnt++; // 找到一条路累加器自增
return ; // 结束这一分支,开始回溯
}
for (int i = 1; i <= N; i++) // 寻找下一个结点
{
if (vis[st][i] == 0 && graph[st][i] != 0 && i != st) // 要求(st,i)未传输过,并且连通
{ // i和st不能相等,否则就不是一条边了
vis[st][i] = 1; // 更改边的使用情况
vis[i][st] = 1; // 无向图,(i,st)也得更新
dfs(i, step - 1); // 递归,深入下一层查找,用最新找到的结点为起点深入,剩余边数减一
vis[st][i] = 0; // 回溯过程,需要将深入查找之前更改的使用状态还原
vis[i][st] = 0; // 同上
}
}
}
public static void main(String[] argv)
{
input = new Scanner(System.in);
N = input.nextInt();
M = input.nextInt();
int a, b;
for (int i = 0; i < M; i++) // 一共M条边
{
a = input.nextInt();
b = input.nextInt();
graph[a][b] = 1; // (a,b)连通
graph[b][a] = 1; // 无向图,(b,a)也连通
}
for (int i = 1; i <= N; i++) // 枚举所有结点,作为起点开始深度搜索
{
dfs(i, 3); // 初始剩余三条边
}
System.out.println(cnt); // 输出累加器结果即为所有结果
}
}
Two:
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
const int MAXN = 10001;
int n, m, cnt = 0;
vector<int> graph[MAXN];
void dfs(int last, int st, int step)
{
if (step == 0)
{
cnt++;
return ;
}
for (int i = 0; i < graph[st].size(); i++)
{
if (graph[st][i] != last)
{
dfs(st, graph[st][i], step - 1);
}
}
}
int main(int argc, const char * argv[])
{
cin >> n >> m;
int a, b;
for (int i = 0; i < m; i++)
{
scanf("%d%d", &a, &b);
graph[a].push_back(b);
graph[b].push_back(a);
}
for (int i = 1; i <= n; i++)
{
dfs(-1, i, 3);
}
cout << cnt << '\n';
return 0;
}
Three:
package javatest;
import java.util.Scanner;
public class NodeTongXin2
{
static class Node // 邻接表结点,实际上是链表结点
{
int v; // 线段端点
Node nt; // 链表的下一个结点
Node() // 实例化时初始化
{
nt = null;
}
Node(int v_, Node nt_) // 实例化时初始化,重载上一个函数
{
v = v_;
nt = nt_;
}
}
static int N, M;
static Node graph[] = new Node[10005]; // 申请一个表头,Node 数组代表表头
static long cnt = 0; // 累加器
private static Scanner input;
static void dfs(int last, int st, int step) // 上一个点,起点,剩余边数
{
if (step == 0) // 剩余0条边后 GG
{
cnt++; // 找到一条路累加器自增
return ; // 结束这一分支,开始回溯
}
Node nd = graph[st].nt; // 查找表头,找到链表起点
for (; nd != null; nd = nd.nt) // 遍历链表
{
if (nd.v != last) // 不能等于上一个结点
{
dfs(st, nd.v, step - 1);
}
}
}
public static void main(String[] argv)
{
input = new Scanner(System.in);
N = input.nextInt();
M = input.nextInt();
for (int i = 0; i <= N; i++)
{
graph[i] = new Node(); // 实例化对象
}
int a, b;
Node nd = null;
for (int i = 0; i < M; i++) // 一共M条边
{
a = input.nextInt();
b = input.nextInt();
nd = new Node(b, graph[a].nt); // 头插法插入链表
graph[a].nt = nd;
nd = new Node(a, graph[b].nt);
graph[b].nt = nd;
}
for (int i = 1; i <= N; i++) // 枚举所有结点,作为起点开始深度搜索
{
dfs(-1, i, 3); // 初始上一个结点为-1,剩余三条边
}
System.out.println(cnt); // 输出累加器结果即为所有结果
}
}
注释只能这么详细了,如果还不懂,那我就只能给你们画图模拟 dfs 每一步的执行过程了,但是我真心不想这样,我没有装 PS……