AOJ-2107 : Can I go there? (いけるかな?)

問題概要

ノード数N(<50)、辺数M(<50)の無向グラフがある。0からN-1への長さZ(<2^31)の経路が存在するか求める問題。ただし同じ辺を連続で使用することはできない。

解法

(直前に居た頂点、今いる頂点)を組にしたグラフを考える。後は行列累乗で計算するだけ。最初見たときはM<50に気づかなかった…。

acceptされたコード

計算量O(M^3 * log Z)。

#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;

typedef bool mat_t;

struct matrix{

	vector< vector<mat_t> > val;
	int height, width;

	matrix(){}
	matrix(int height_, int width_):height(height_), width(width_){
		val.assign(height, vector<mat_t>(width, false));
	}

	vector<mat_t>& operator[](const size_t ind){
		return val[ind];
	}
};

matrix get_identity(int size){
	matrix ret(size, size);
	for(int i=0; i<size; i++){
		ret[i][i] = true;
	}
	return ret;
}

matrix operator*(const matrix& lhs, const matrix& rhs){
	matrix ret(lhs.height, rhs.width);

	for(int i=0; i<lhs.height; i++){
		for(int k=0; k<lhs.width; k++){
			for(int j=0; j<rhs.width; j++)if(lhs.val[i][k] && rhs.val[k][j]){
				ret[i][j] = true;
			}
		}
	}

	return ret;
}

matrix pow(const matrix& x, const int k){
	matrix ret = get_identity(x.width), base = x;

	for(int e=k; e; e>>=1){
		if(e&1){
			ret = ret * base;
		}
		base = base * base;
	}


	return ret;
}

const int MAX_N = 50;
const int MAX_M = 50;

int N, M, Z;

pair<int,int> edge[2*MAX_M];
bool after[2*MAX_M], result[2*MAX_M];

bool init(){
	scanf("%d%d%d", &N, &M, &Z);
	for(int i=0; i<M; i++){
		int x, y;
		scanf("%d%d", &x, &y);
		x--; y--;
		edge[2*i + 0] = make_pair(x, y);
		edge[2*i + 1] = make_pair(y, x);
	}
	return N > 0;
}

bool solve(){
	matrix mat(2*M, 2*M);
	for(int i=0; i<2*M; i++){
		for(int j=0; j<2*M; j++)if(i!=j){
			if(edge[i].second == edge[j].first && edge[i].first != edge[j].second){
				mat[i][j] = true;
			}
		}
	}
	mat = pow(mat, Z-1);

	memset(after, false, sizeof(after));
	for(int i=0; i<2*M; i++){
		if(edge[i].first == 0){
			after[i] = true;
		}
	}

	memset(result, false, sizeof(result));
	for(int i=0; i<2*M; i++){
		for(int j=0; j<2*M; j++){
			result[j] |= mat[i][j] && after[i];
		}
	}

	for(int i=0; i<2*M; i++)if(result[i] && edge[i].second == N-1){
		return true;
	}

	return false;
}

int main(){
	for(;init();){
		puts(solve() ? "yes" : "no");
	}

	return 0;
}