公式

E - A Path in A Dictionary 解説 by mechanicalpenciI


まず、次のことが成り立ちます。

単純無向グラフ \(G\) において、頂点 \(u,v\) と頂点集合 \(S\) が与えられた時、\(u\) から \(v\) まで \(S\) に属する頂点を通らずに到達可能な(= \(S\) に属する頂点を含まない、\(u\) から \(v\) への(単純とは限らない)パスが存在する)ことと、 \(S\) に属する頂点を含まない、\(u\) から \(v\) への単純パスが存在することは同値である。

\(S\) に属する頂点を含まない、\(u\) から \(v\) への単純パスが存在する」ならば「\(u\) から \(v\) まで \(S\) に属する頂点を通らずに到達可能である」ことは明らかです。逆を証明します。
これは、 \(S\) に属する頂点を含まない、\(u\) から \(v\) への(単純とは限らない)パス \(P=(P_1,P_2,\ldots, P_{\lvert P\rvert})\) に対して、\(k=1,2,\ldots,N\) について次の手順を繰り返すことで単純パスを得ることができることから従います。

  • \(P\) に頂点 \(k\) が高々 \(1\) 回しか登場しないとき、何もしない。
  • \(P\) に頂点 \(k\) が複数回登場するとき、\(u\) から初めて頂点 \(k\) が登場するまでのパスと、最後に頂点 \(k\) が登場してから \(v\)までのパスを繋げ、これを新たな \(P\) とする。

このとき、手順によって、パスにおける各頂点の登場回数が増えることはなく、頂点 \(k\) の登場回数はその頂点についての手順のあと高々 \(1\) 回になることから、最終的に得られる \(P\)\(u\)\(v\) を結ぶ単純パスとなります。

次に、\(U\) から \(V\) へ辞書順最小の単純パスで向かうためにどのように移動する(次の頂点を選ぶ)かについて考えます。

最初の頂点は \(U\) です。 \(U\)と直接辺で結ばれている頂点 \(x\) について、\(P_1=U\), \(P_2=x\) であるような\(V\) への単純パス \(P\) が存在する必要十分条件は、\(U\) を含まない、\(x\) から \(V\) への単純パスが存在することであり、これは上の命題から \(x\) から \(V\) まで \(U\) を通らずに到達可能であることと同値です。また、\(G\) は無向グラフであることから、これは \(V\) から \(x\) まで \(U\) を通らずに到達可能であることと同値です。よって、答えとなるパスにおいて \(U\) から移動する頂点は、\(U\)と直接辺で結ばれており、さらに \(V\) から \(x\) まで \(U\) を通らずに到達可能であるような頂点 \(x\) である必要があります。逆に、答えとなるパスにおいて \(U\) から移動する頂点はその条件をみたす頂点のうち最も番号が小さいものです。なぜなら、\(P_1=U\), \(P_2=x\) であるような\(V\) への単純パス \(P\) が存在すとき、他の任意の\(P'_1=U\), \(P'_2=x'\) \((x<x')\) であるような\(V\) への単純パス \(P'\) について \(P\)\(P'\) より辞書順で小さくなるからです。

以下、\(V\) に到達する(ある \(k\) において\(P_k=V\) となる)まで貪欲に単純パスを決定することができます。すなわち、\(P_1,P_2,\ldots,P_k\) が決まっている時、\(P_{k+1}\)\(P_k\)と直接辺で結ばれており、さらに \(V\) から \(x\) まで \(P_1,P_2,\ldots,P_k\) を通らずに到達可能であるような頂点 \(x\) のうち最も番号が小さいものです。
同じ頂点は選ばれないことからこの操作は有限回で終了し、また次の頂点の選び方から、操作の途中で \(P_{k+1}\) としての条件をみたす頂点が存在しなくなることもありません。 このようにして答えを求めることができます。

各テストケースの時間計算量は各 \(k\) において、\(V\) から\(P_1,P_2,\ldots,P_k\) を通らずに到達可能であるような頂点の列挙に深さ優先探索 (DFS) などで \(O(N+M)\), \(k\) が高々 \(1\) 以上 \(N-1\) 以下の整数の範囲しか動かないため、全体での計算量は \(O(N(N+M))\) となります。入力全体においても、\(\Sigma_iN_i(N_i+M_i)\leq (\Sigma_iN_i)((\Sigma_iN_i)+(\Sigma_iM_i))\) が成り立つため、本問題の制約下で十分高速です。
よって、この問題を解くことができました。

c++ による実装例:

#include <bits/stdc++.h>
using namespace std;

#define rep(i, n) for(int i = 0; i < n; ++i)
#define N 1000

int n,m,x,y;
vector<int>e[N];
vector<int>ans;
bool used[N];
bool cand[N];

void dfs(int k){
	cand[k]=true;
	int sz=e[k].size();
	rep(i,sz){
		if((!cand[e[k][i]])&&(!used[e[k][i]]))dfs(e[k][i]);
	}
	return;
}

void solve(void){
	ans.clear();
	int cur = x;
	while(cur!=y){
		ans.push_back(cur);
		used[cur]=true;
		rep(i,n)cand[i]=false;
		dfs(y);
		int sz=e[cur].size();
		int nxt=n;
		rep(i,sz)if(cand[e[cur][i]])nxt=min(nxt,e[cur][i]);
		cur=nxt;	
	}
	ans.push_back(y);
	return;
}


int main(void){
	int t;
	cin >> t;
	rep(_,t){
		cin >> n >> m >> x >> y;
		x--,y--;
		rep(i,n)used[i]=false;
		int u,v;
		rep(i,m){
			cin >> u >> v;
			e[u-1].push_back(v-1);
			e[v-1].push_back(u-1);
		}
		solve();
		int sz = ans.size();
		rep(i,sz-1)cout << (ans[i]+1) << " ";
		cout << (ans[sz-1]+1) << endl;
		rep(i,n)e[i].clear();
	}
	return 0;
}

投稿日時:
最終更新: