29 E Quarrel

問題

A,Bの二人がそれぞれグラフの頂点1,頂点nにいる。
二人は喧嘩しているので、同時に同じ頂点にいることはできない。


Aが頂点nまで、Bが頂点1まで以下の制約を満たしながら移動する。

  • 二人はそれぞれ1秒に1回、グラフの隣の頂点に必ず移動する
  • 同じ頂点を何回通ってもいい
  • 二人は同時に同じ頂点にいてはいけないが、辺上ですれ違うことは可能
  • 二人は同時にゴールする。途中でゴールを通過するのは可能

このとき、二人がゴールにつくまでの最短経路を求めよ。

制約条件

n≦500
m≦10000

方針

明らかに無理な場合は不可能を返す。それ以外全探索。
ただし愚直に全探索すると遅い気がするのでA*探索を使った。


明らかにどうやっても無理なのは、

  • 始点と終点が非連結
  • 始点から終点まで、単純パスが1通りしかない
  • かつそのパスの長さが偶数長
  • かつそのパスに長さ2以上の側鎖っぽいのがついてない

この場合だけ無理と事前に判定してやる。
これはmaxFlowを流して、流量が1であることを確認して、側鎖を全部見ることで行った。


無理そうじゃない場合はA*で探索する。
コストの見積もりはmax(Aのゴールまでの距離,Bのゴールまでの距離)
ゴールまでの距離はワーシャルフロイドであらかじめ求めた。


なんか無理矢理な方針な気がする。

ソースコード

maximumFlowの部分は省略。

int n,m,d[500][500];
Graph e;
bool visit[500],use[500][500];
int pa[500][500],pb[500][500],cost[500][500];

int rec(int c){
	visit[c]=1;
	int ret=1;
	fr(i,e[c])if(!visit[i->dst])ret+=rec(i->dst);
	return ret;
}

bool can()
{
	scanf("%d%d",&n,&m);
	e.clear(); e.resize(n);
	rep(i,n)rep(j,n)d[i][j]=i==j?0:1e9;
	rep(i,m){
		int s,t; scanf("%d%d",&s,&t);
		s--; t--;
		e[s].pb(Edge(s,t,1)); e[t].pb(Edge(t,s,1));
		d[s][t]=d[t][s]=1;
	}
	int flow=maximumFlow(e,0,n-1);
	//dbg(flow);
	
	if(flow==0)return 0;
	if(flow>1)return 1;
	
	vi prev(n,-1), path;
	queue<int> q; q.push(0);
	while(!q.empty()){
		int c=q.front(); q.pop();
		visit[c]=1;
		if(c==n-1)break;
		fr(i,e[c])if(prev[i->dst]<0)prev[i->dst]=c, q.push(i->dst);
	}
	int cur=n-1;
	for(;cur!=0;cur=prev[cur])path.pb(cur);
	path.pb(0);
	
	if(path.size()%2==0)return 1;
	
	rep(i,n)visit[i]=0;
	rep(i,path.size())visit[i]=1;
	fr(i,path)fr(j,e[*i])if(!visit[j->dst]){
		int ret=rec(j->dst); //dbg(ret);
		if(ret>1)return 1;
	}
	return 0;
}
void run(){
	if(!can()){
		//cerr<<"cant"<<endl;
		cout<<-1<<endl; return;
	}
	
	rep(k,n)rep(i,n)rep(j,n)d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
	rep(i,n)rep(j,n)pa[i][j]=pb[i][j]=-1, cost[i][j]=1e9, use[i][j]=0;
	
	priority_queue<pair<int,pi> > q;
	q.push(mp(-d[0][n-1],mp(0,n-1)));
	cost[0][n-1]=0;
	
	while(!q.empty()){
		int a=q.top().second.first, b=q.top().second.second;
		//dbg(a); dbg(b);
		q.pop();
		if(use[a][b])continue;
		use[a][b]=1;
		if(a==n-1&&b==0)break;
		
		fr(i,e[a])fr(j,e[b])if(i->dst!=j->dst&&cost[i->dst][j->dst]>cost[a][b]+1)
		pa[i->dst][j->dst]=a, pb[i->dst][j->dst]=b, cost[i->dst][j->dst]=cost[a][b]+1,
		q.push(mp(-max(d[i->dst][n-1],d[j->dst][0])-cost[a][b],mp(i->dst,j->dst)));
	}
	if(cost[n-1][0]==1e9){
		cout<<-1<<endl; return;
	}
	
	vi p,pp;
	int a=n-1,b=0;
	for(;a!=0||b!=n-1;){
		p.pb(a), pp.pb(b);
		int na=pa[a][b], nb=pb[a][b];
		a=na; b=nb;
	}
	p.pb(0); pp.pb(n-1);
	
	reverse(all(p)); reverse(all(pp));
	printf("%d\n",(int)p.size()-1);
	rep(i,p.size())printf("%d ",p[i]+1); puts("");
	rep(i,p.size())printf("%d ",pp[i]+1); puts("");
}