UTPC2013 L 1円ロード

問題

日本語なので本文参照(http://utpc2013.contest.atcoder.jp/tasks/utpc2013_12


重みつき有向グラフが与えられる。
各辺には+, -, =の3種類があって、

  • +の辺を通ると所持金が1円増える
  • -の辺を通ると所持金が1円減る(所持金0のときは通れない)
  • =の辺を通ると所持金は変化しない


0円でスタートして0円でゴールする最短距離を求めよ。

制約条件

頂点≦250

方針

kinaba先生の素晴らしい解説スライド参照(http://www.kmonos.net/wlog/sub/utpc13-L.pdf


0円以上を所持し続けて0円でゴールするルートは、
最初に = を通って、0円→0円のルートを通ってゴールするか、
最初に + を通って0円→0円のルートを通って、最後にちょうど - を通ってゴールするしかない。


この0円→0円の最短距離の表を埋めようとすると、両側から更新しないといけないので、
O(N^4)とかもっとかかりそう。
なので、工夫が必要で、具体的には0円→-1円の最短距離も表にしておけばいい。


それで、ワーシャルフロイド的にやろうとすると解説スライドの原理でNGなので、
距離の短い順にテーブルを確定させていかなければならない。

#define F first
#define S second
 
const int N = 250;
int n;
 
int edge[N][N], dist[N][N];
int dp[2][N][N], inqbest[2][N][N];
 
inline void add(priority_queue<pair<pi, pi> > &q, int t, int a, int b, int d){
	if(dp[t][a][b] <= d) return;
	if(inqbest[t][a][b] <= d) return;
	
	inqbest[t][a][b] = d;
	q.push(mp(mp(-d, t), mp(a, b)));
}
 
int main(){
	
	cin >> n;
	rep(i, n){
		string type;
		cin >> type;
		rep(j, n){
			if(type[j] == 'x') edge[i][j] = inf;
			else if(type[j] == '=') edge[i][j] = 0;
			else edge[i][j] = type[j] == '+' ? 1 : -1;
		}
	}
	rep(i, n) rep(j, n) cin >> dist[i][j];
	
	rep(k, 2) rep(i, n) rep(j, n) dp[k][i][j] = inqbest[k][i][j] = inf;
	
	//dist, type, s, t
	//type = 0なら0円→0円のルート、type = 1なら0円→-1円のルート
	priority_queue<pair<pi, pi> > q;
	rep(i, n) q.push(mp(mp(0, 0), mp(i, i)));
	
	while(!q.empty()){
		int d = -q.top().F.F, t = q.top().F.S;
		int a = q.top().S.F, b = q.top().S.S;
		q.pop();
		
		if(dp[t][a][b] <= d) continue;
		dp[t][a][b] = d;
		if(t == 0 && a == 0 && b == n - 1) break;
		
		//ルートの先頭に1本道を付け足す
		rep(i, n) if(edge[i][a] < inf){
			int nt = t - edge[i][a];
			if(nt < 0 || nt > 1) continue;
			
			add(q, nt, i, b, d + dist[i][a]);
		}
		//ルートの末尾に1本道を付け足す
		rep(i, n) if(edge[b][i] < inf){
			int nt = t - edge[b][i];
			if(nt < 0 || nt > 1) continue;
			if(t == 1 && nt == 0) continue;
			
			add(q, nt, a, i, d + dist[b][i]);
		}
		
		//ルートとルートを合体させる。これがないと通らない。
		//0円→5円→0円→3円→0円みたいに遷移するルートが作れないから。
		//0円→1円のルートも覚えされば不要かも?
		rep(tt, 2 - t){
			rep(i, n){
				add(q, t + tt, a, i, d + dp[tt][b][i]);
				add(q, t + tt, i, b, d + dp[tt][i][a]);
			}
		}
	}
	int ans = dp[0][0][n - 1];
	cout << (ans == inf ? -1 : ans) << endl;
	
	return 0;
}