TopCoder SRM 360 Div1 Medium PrinceOfPersia

問題

hxwのグリッドで表される迷路がある。
迷路の中に二人がいる。


maze[i][j]が'#'であるマスは壁で、進入することができない。
maze[i][j]が'.'であるマスは何もないマスで、進入することができる。
maze[i][j]が'P'のマス(迷路上に二つ)は、二人のスタートの位置を表す。


二人は、上下左右に隣接するマスへ移動することを繰り返して、同じマスへ到達する。
二人が同じマスへ到達できないように、迷路上にいくつかブロックを置く。


最低でいくつのブロックをおく必要があるか、求めよ。

制約条件

h≦10
w≦10

方針

フロー。
各マスにINおよびOUTの頂点を作り、
INからOUTへ容量1の辺を張る。
各マスのOUTから、隣接するマスのINへ容量1の辺を張る。


一方のPのマスのOUTを湧き出し、もう一方のPのマスのINを吸い込みとして、最大流を流せば、最大流=最小カットの関係から、流量が置くべきブロックの数となる。

ソースコード

最大流は蟻本実装。

const int MAX_V=202;
struct edge{ int to,cap,rev; };
vector<edge> G[MAX_V];

int level[MAX_V],iter[MAX_V];

void add_edge(int from,int to,int cap){
	G[from].pb((edge){ to, cap, G[to].size() });
	G[to].pb((edge){ from, 0, G[from].size()-1 });
}

void bfs(int s){
	memset(level,-1,sizeof(level));
	queue<int> q;
	level[s]=0;
	q.push(s);
	while(!q.empty()){
		int v=q.front(); q.pop();
		rep(i,G[v].size()){
			edge &e=G[v][i];
			if(e.cap>0&&level[e.to]<0){
				level[e.to]=level[v]+1;
				q.push(e.to);
			}
		}
	}
}
int dfs(int v,int t,int f){
	if(v==t)return f;
	for(int &i=iter[v];i<G[v].size();i++){
		edge &e=G[v][i];
		if(e.cap>0&&level[v]<level[e.to]){
			int d=dfs(e.to,t,min(f,e.cap));
			if(d>0){
				e.cap-=d;
				G[e.to][e.rev].cap+=d;
				return d;
			}
		}
	}
	return 0;
}
int max_flow(int s,int t){
	int flow=0;
	for(;;){
		bfs(s);
		if(level[t]<0)return flow;
		memset(iter,0,sizeof(iter));
		int f;
		while((f=dfs(s,t,inf))>0)flow+=f;
	}
}
const int dy[]={-1,0,1,0},dx[]={0,-1,0,1};

class PrinceOfPersia {
	public:
	int minObstacles(vector <string> maze) 
	{
		int h=maze.size(), w=maze[0].size();
		int V=h*w*2+2, cnt=0;
		
		rep(i,V)G[i].clear();
		rep(i,h)rep(j,w)add_edge(i*w+j<<1,i*w+j<<1|1,1);
		
		rep(i,h)rep(j,w)if(maze[i][j]!='#'){
			if(maze[i][j]=='P'){
				if(++cnt==1)add_edge(V-2,i*w+j<<1|1,inf);
				else add_edge(i*w+j<<1,V-1,inf);
			}
			rep(d,4){
				int y=i+dy[d], x=j+dx[d];
				if(y<0||x<0||y>=h||x>=w)continue;
				if(maze[i][j]=='P'&&maze[y][x]=='P')return -1;
				if(maze[y][x]!='#')add_edge(i*w+j<<1|1,y*w+x<<1,1);
			}
		}
		return max_flow(V-2,V-1);
	}
};

最初、接続関係をもつDPだと思って頑張って書いたがTLE...

int h,w;
map<pair<ll,int>,int> dp[11];

inline void merge(vi &v,int a,int b){
	int t=v[b];
	rep(i,v.size())if(v[i]==t)v[i]=v[a];
}
inline void reorder(vi &v){
	int to[11]={},k=0;
	rep(i,v.size())if(v[i]&&!to[v[i]])to[v[i]]=++k;
	rep(i,v.size())if(v[i])v[i]=to[v[i]];
}
inline ll packv(vi &a){
	ll base=1, res=0;
	rep(i,a.size())res+=a[i]*base, base*=11;
	return res;
}
inline int packp(vi &p){
	int base=1, res=0;
	rep(i,p.size()){
		res+=base*(p[i]+1);
		base=50;
	}
	return res;
}

class PrinceOfPersia {
	public:
	int minObstacles(vector <string> maze) 
	{
		h=maze.size(); w=maze[0].size();
		rep(i,11)dp[i].clear();
		
		vi v,p;
		rep(i,w)v.pb(i+1);
		dp[0][mp(packv(v),packp(p))]=0;
		rep(i,h)fr(it,dp[i]){
			v.clear(); p.clear();
			for(ll j=0, t=it->first.first;j<w;j++, t/=11)v.pb(t%11);
			if(it->first.second/50)p.pb(it->first.second/50-1);
			if(it->first.second%50)p.pb(it->first.second%50-1);
			
			rep(mask,1<<w){
				int cur[11]={}, to[11]={}, ncost=it->second, np_;
				ll nv_;
				vi nv,np;
				
				rep(j,w){
					if(maze[i][j]!='.'&&(mask&1<<j))goto NEXT;
					if(maze[i][j]=='#'||(mask&1<<j))cur[j]=1;
				}
				rep(j,w)nv.pb(cur[j]?0:j+1);
				//merge
				rep(j,w)rep(k,j)if(!cur[j]&&!cur[k]&&v[j]&&v[j]==v[k])merge(nv,j,k);
				rep(j,w-1)if(!cur[j]&&!cur[j+1])merge(nv,j,j+1);
				
				reorder(nv);
				rep(j,w){
					if(nv[j])to[v[j]]=nv[j];
					if(mask&1<<j)ncost++;
				}
				to[0]=0;
				rep(j,p.size())np.pb(to[p[j]]);
				rep(j,w)if(maze[i][j]=='P')np.pb(nv[j]);
				
				if(np.size()>1&&np[0]&&np[0]==np[1])continue;
				
				//rep(j,w)cerr<<nv[j]<<(j==w-1?"\n":" ");
				nv_=packv(nv); np_=packp(np);
				
				if(!dp[i+1].count(mp(nv_,np_))||dp[i+1][mp(nv_,np_)]>ncost)
				dp[i+1][mp(nv_,np_)]=ncost;
				
				NEXT:;
			}
		}
		
		int ans=inf;
		fr(it,dp[h]){
			p.clear();
			if(it->first.second/50)p.pb(it->first.second/50-1);
			if(it->first.second%50)p.pb(it->first.second%50-1);
			//rep(i,p.size())cerr<<p[i]<<(i==p.size()-1?"\n":" ");
			//dbg(it->second);
			if(p[0]==0||p[0]!=p[1])ans=min(ans,it->second);
		}
		return ans==inf?-1:ans;
	}
};