藍橋杯-歷屆試題-PREV-13-網路尋路
阿新 • • 發佈:2018-12-27
描述
題解
很簡單的一道題,但是前天上 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……