SRM 446 DIV1 Medium AntOnGraph
http://apps.topcoder.com/stat?c=problem_statement&pm=10516&rd=13900
問題
重み付き有向グラフ(V <= 50, -499 <= w <= 499)がある。
今、点0に蟻が居る。
蟻は1秒間にstepsPerSecond(<=100)回動き続けて(その場に立ち止まることはできない)
最大timeLimit(<=10^9)秒間動くことが出来る(秒の境目なら動くのをやめることができる)
蟻は最終的に点1に行きたい。
ここで、蟻が通った辺の重みの合計を最大化したい。
もし点1にたどり着くことができないのなら"IMPOSSIBLE"と返せ
回答
他の人の回答を参考にして、考えて、わかった。
まず、蟻が1回動くことは、その時点での全点間の最大重み(全点間最短距離と上下を入れ替えれば同じ、以下最大としてを考える)を求めればいい。これにはWarshall-Floyd法が使える。
ただし、このままではtimeLimit(<=10^9)が明らかに大きすぎる。
ここで、Warshall-Floyd法が行列の乗算と似た構造を持っていることに注目する。
実際に行列の乗算の、(+)をmaxに、(*)を(+)に変えたものがWarshall-Floydっぽくなる。
蟻が何回か動くことは行列の累乗として考えられる。
また、(-∞, max, (+))を利用すると半環になっていて、また、その行列も累乗の高速化アルゴリズム(バイナリ法)が使える構造になっている(詳細はよくわかって/調べてないが)
そのため、バイナリ法を使ってO(V^3 (log stepsPerSecond + log timeLimit))くらいで出来る。
後はtimeLimit以下、ということの扱いだが、これは一旦stepsPerSecond乗を計算した後、いつでも点1にとどまれるように、1→1のコスト0の辺を追加してやればいい。ただし、点1を含む負閉路(この場合は正閉路)があればその方が良いので、単に今の1→1のweightがマイナスの時のみ0にしてやればいい
コード
#include <string> #include <vector> #include <algorithm> #include <set> #include <map> #include <queue> #include <iostream> #include <sstream> #include <numeric> #include <cstdio> #include <cmath> #include <ctime> #include <cstring> #define rep(i,n) for(int (i)=0;(i)<(int)(n);++(i)) #define rer(i,l,u) for(int (i)=(int)(l);(i)<=(int)(u);++(i)) #define reu(i,l,u) for(int (i)=(int)(l);(i)<(int)(u);++(i)) #define aut(r,v) typeof(v) r = (v) #define each(it,o) for(aut(it,o.begin()); it != (o).end(); ++ it) #define all(o) (o).begin(), (o).end() #define pb(x) push_back(x) #define mp(x,y) make_pair((x),(y)) #define mset(m,v) memset(m,v,sizeof(m)) #define INF 0x3f3f3f3f3f3f3f3fLL using namespace std; typedef long long ll; typedef vector<int> vi; typedef pair<int,int> pii; typedef ll Number; //base struct MaxPlus { Number t; struct MinusInf_tag {}; explicit MaxPlus(Number x):t(x){} explicit MaxPlus(MinusInf_tag):t(-INF){} MaxPlus() {} inline bool isMinInf() const {return t==-INF;} MaxPlus operator+(const MaxPlus& y) const { if(isMinInf())return y; else if(y.isMinInf()) return *this; else return MaxPlus(max(t, y.t)); } MaxPlus operator*(const MaxPlus& y) const { if(isMinInf() || y.isMinInf()) return MaxPlus(MaxPlus::MinusInf_tag()); else return MaxPlus(t + y.t); } }; typedef MaxPlus MatT; MatT Mat_zero = MaxPlus(MaxPlus::MinusInf_tag()), Mat_one = MaxPlus(0); typedef vector<MatT> MatV; typedef vector<MatV> Mat; Mat mult(const Mat& A, const Mat& B) { int N = A.size(); Mat C(N, MatV(N)); rep(i, N) rep(j, N) { C[i][j] = Mat_zero; rep(k, A[i].size()) { C[i][j] = C[i][j] + A[i][k] * B[k][j]; } } return C; } Mat ident(int N) { Mat A(N, MatV(N)); rep(i, N) { rep(j, N) A[i][j] = Mat_zero; A[i][i] = Mat_one; } return A; } Mat power(const Mat& A, int p) { if(p == 0) return ident(A.size()); else if(p == 1) return A; else { Mat B = power(mult(A, A), p >> 1); if(p & 1) B = mult(B, A); return B; } } struct AntOnGraph { string maximumBonus(vector <string> p0, vector <string> p1, vector <string> p2, int stepsPerSecond, int timeLimit) { int n = p0.size(); Mat m(n, MatV(n)); rep(i, n) rep(j, n) { string s; s += p0[i][j]; s += p1[i][j]; s += p2[i][j]; if(s == "000") { m[i][j] = Mat_zero; }else { m[i][j] = MaxPlus(atoi(s.c_str()) - 500); } cout << "m["<<i<<"]["<<j<<"] = "<<m[i][j].t<<endl; } m = power(m, stepsPerSecond); //問題わかってなかったが、動く秒数はtimeLimit以下なら何秒でも良いため、 //つまりその場に留まる辺を追加してあげる if(m[1][1].t < 0) m[1][1] = MaxPlus(0); m = power(m, timeLimit); if(m[0][1].isMinInf()) return "IMPOSSIBLE"; stringstream s; s << m[0][1].t; return s.str(); } };