1. 程式人生 > 其它 >E. Nearest Opposite Parity_反向建圖+超級源點

E. Nearest Opposite Parity_反向建圖+超級源點

E. Nearest Opposite Parity_反向建圖+超級源點

題目大意

給數列a,ai表示存在(i,i-ai)和(i,i+ai)兩條邊。每條邊邊權為1。現在要求出以ai出發到aj(ai和aj滿足奇偶性不同的條件)的所有路徑中最短的長度。

思路和程式碼

超級源點模板題

首先建立兩個超級源點。分別代表從奇數出發從偶數出發

再反向建圖,這樣dsi的含義就變成到達點i所需要的最短路徑,而不是常規最短路問題中的dsi表示從起點到i的最短路徑。

再分別做一次最短路(可以dij也可以bfs啥的)

然後把從奇數點出發的填入偶數位置,把從偶數點出發的填入奇數位置。

int n , m , k ; 

void dij(int s , vct<ll> &ds , vct<vct<pii> > &eg){
	rep(i , 0 , n + 1) ds[i] = INF ;
	vct<bool> vis(n + 2 , 0) ;
	
	priority_queue<pll , vct<pll> , greater<pll> > hp ;
	hp.push({0 , s}) ;
	ds[s] = 0 ;
	
	while(hp.size()){
		int now = hp.top().se ;
		int dis = hp.top().fi ;
		hp.pop() ;
		if(vis[now]) continue ;
		vis[now] = 1 ;
//		cout << now << "\n" ;
		for(pii e : eg[now]){
			int nxt = e.se ;
			int len = e.fi ;
			if(ds[nxt] > ds[now] + len){
				ds[nxt] = ds[now] + len ;
				hp.push({ds[nxt] , nxt}) ;
			}
		}
	}
}

void solve(){
	
	cin >> n ;
	vct<int> a(n + 1 , 0) ;
	vct<vct<pii> > eg(n + 2) ;
	
	rep(i , 1 , n){
		cin >> a[i] ;
		if(a[i] % 2 == 1) eg[0].pb({0 , i}) ;
		else  eg[n + 1].pb({0 , i}) ;
		//0為奇數起點 , n+1為偶數起點
		if(i + a[i] <= n) eg[i + a[i]].pb({1 , i}) ;

		if(i - a[i] >= 1) eg[i - a[i]].pb({1 , i}) ;
	}

	vct<ll> ds(n + 2 , INF) ;
	
	vct<ll> ans(n + 1 , -1) ;
	
	dij(0 , ds , eg) ;
	
	rep(i , 1 , n)
	if(a[i] % 2 == 0){//奇數點要到偶數點
		if(ds[i] >= INF) continue ;
		ans[i] = ds[i] ;
	}
	
	dij(n + 1 , ds , eg) ;

	rep(i , 1 , n)
	if(a[i] % 2){//偶數點要到奇數點
		if(ds[i] >= INF) continue ;
		ans[i] = ds[i] ;
	}
		
	show1(ans , 1 , n) ;
	
}//code_by_tyrii 

小結

關於這一題我一開始想成了將所有點都和超級源點相連,但是這樣就會導致所有點的ds值都是0 。

但是仔細一想,我們要的只是{偶數點到奇數點,奇數點到偶數點}兩種情況,那麼分別將超級源點和奇數偶數點相連即可。

比較好玩的題目