1. 程式人生 > 實用技巧 >Topcoder 12519 ScotlandYard(點對 dp+最長路)

Topcoder 12519 ScotlandYard(點對 dp+最長路)

題面傳送門

題意:
有兩個人 A 和 B 玩一個遊戲。遊戲規則大致是這樣的:
\(n\) 個城市和三種交通工具公交、地鐵和計程車。
給出三個 \(n\times n\) 的字元矩陣 \(b,m,t\)\(b_{i,j}='Y'\) 表示從城市 \(i\) 可以通過公交到達城市 \(j\)\(b_{i,j}='N'\) 表示從城市 \(i\) 不可以通過公交到達城市 \(j\)\(m,t\) 同理。
現在 A 要選擇一個起始城市 \(i\),但是 B 不知道這個城市的編號。小 A 每次可以通過某種交通工具到達另一個城市,並將乘坐的交通工具告訴 B。如果 B 猜出了小 A 當前所在的城市,或者小 B 動不了了,遊戲結束。
A 的得分為 A 經過的邊數。求 A 得分的最大值,如果遊戲可能永遠進行下去,輸出 -1。
\(1 \leq n \leq 50\)

話說這題出現了兩次呢。。。記得 CSP 前一週學校模擬賽某道題就是這個,結果昨天模擬賽又出現了一遍。。。今天終於見到這題的真面貌了(
\(S\) 為當前 A 當前可能位於的城市的集合。初始 \(S=\{1,2,\dots,n\}\)
如果小 A 搭乘了交通工具 \(x\),那麼 \(S\) 就變為:\(S\) 中的點集通過交通工具 \(x\) 能到達的點。
答案就是最多進行多少次操作後 \(S\) 中剩一個點。
很容易想到 \(2^n\) 的做法,對於集合之間的相互轉化關係連邊,跑最長路。


其實並不用把集合中每個點都表示出來。
只需要找到兩個代表點 \((i,j)\),對這些點對連邊

跑最長路就行了。
這樣點的個數就降到了 \(n^2\)
至於 \(-1\) 的情況,就是判斷對點對建立的圖中有沒有環,記憶化搜尋/拓撲排序就可以了。
時間複雜度 \(n^4\),是道思維題(為啥就想不出來呢,wtcl)

#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fz(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define ffe(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
typedef pair<int,int> pii;
typedef long long ll;
const int MAXN=50+5;
int n,dp[MAXN][MAXN],vis[MAXN][MAXN];
bool s1[MAXN][MAXN],s2[MAXN][MAXN],s3[MAXN][MAXN];
int dfs(int x,int y){
	if(x>y) swap(x,y);
	if(x==y) return 0;
	if(vis[x][y]) return 1e9;
	if(~dp[x][y]) return dp[x][y];
	vis[x][y]=1;dp[x][y]=0;
	for(int i=1;i<=n;i++) for(int j=1;j<=n;j++){
		if((s1[x][i]&&s1[y][j])||(s1[x][i]&&s1[x][j])||(s1[y][i]&&s1[y][j])||
		   (s2[x][i]&&s2[y][j])||(s2[x][i]&&s2[x][j])||(s2[y][i]&&s2[y][j])||
		   (s3[x][i]&&s3[y][j])||(s3[x][i]&&s3[x][j])||(s3[y][i]&&s3[y][j]))
				dp[x][y]=max(dp[x][y],dfs(i,j)+1);
	}
	vis[x][y]=0;return dp[x][y];
}
class ScotlandYard{
public:
	int maxMoves(vector<string> taxi,vector<string> bus,vector<string> metro){
		n=taxi.size();
		for(int i=0;i<n;i++) for(int j=0;j<n;j++) s1[i+1][j+1]=(taxi[i][j]=='Y');
		for(int i=0;i<n;i++) for(int j=0;j<n;j++) s2[i+1][j+1]=(bus[i][j]=='Y');
		for(int i=0;i<n;i++) for(int j=0;j<n;j++) s3[i+1][j+1]=(metro[i][j]=='Y');
		int ans=0;memset(dp,-1,sizeof(dp));
		for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) ans=max(ans,dfs(i,j));
		if(ans>=1e9) return -1;return ans;
	}
};
//ScotlandYard program;
//int main(){
//	int n;scanf("%d",&n);
//	vector<string> taxi,bus,metro;
//	for(int i=1;i<=n;i++){string s;cin>>s;taxi.pb(s);}
//	for(int i=1;i<=n;i++){string s;cin>>s;bus.pb(s);}
//	for(int i=1;i<=n;i++){string s;cin>>s;metro.pb(s);}
//	printf("%d\n",program.maxMoves(taxi,bus,metro));
//	return 0;
//}