DFS
版本1
首先用map<string,int>
將字符串變量映射爲數字。
建圖:由於題目中,並沒有數據都不爲0,所以我乾脆用bool g[][]
來記錄邊的存在,用double graph[][]
來記錄邊權,並且建成雙向圖。
dfs:
用bool vis[]
記錄訪問的點,防止反覆橫跳,無限遞歸下去。
回溯的時候,注意還原現場。
class Solution {
public:
map<string,int> m;
double graph[1010][1010] = {0};
bool g[1010][1010] = {0},vis[1010] = {0};
int cnt = 0;
vector<double> calcEquation(vector<vector<string>>& equations, vector<double>& values, vector<vector<string>>& queries) {
for(int i=0;i<equations.size();i++){
vector<string> &pair = equations[i];
int x,y;
if(m.count(pair[0])){
x = m[pair[0]];
}else{
m[pair[0]] = ++cnt;
x = cnt;
}
if(m.count(pair[1])){
y = m[pair[1]];
}else{
m[pair[1]] = ++cnt;
y = cnt;
}
g[x][y] = g[y][x] =1;
graph[x][y] = values[i];
graph[y][x] = values[i]==0? -1:1.0/values[i];
}
vector<double> ans;
for(auto pair:queries){
string &s1 = pair[0];
string &s2 = pair[1];
if(!m.count(s1) || !m.count(s2)){
ans.push_back(-1);
continue;
}
int x = m[s1];
int y = m[s2];
memset(vis,0,sizeof(vis)); //多次dfs,多次清空標記數組。
double res = 1;
if(!dfs(x,y,res)){ //先判斷是否連通
ans.push_back(-1);
}else{
ans.push_back(res);
}
}
return ans;
}
bool dfs(int x,int y,double &res){
if(x==y){
return true;
}
vis[x] = 1;
for(int i=1;i<=cnt;i++){
if(g[x][i] && !vis[i]){
res*=graph[x][i];
if(dfs(i,y,res)){
return true;
}
res/=graph[x][i];
vis[i] = 0;
}
}
return false;
}
};
版本2
上個版本的DFS是將字符串映射到數字,看了網友的做法,其實也沒有必要將字符串映射成數字去處理。
如何建圖:
如果用鄰接矩陣存儲圖的話,int graph[][]
用map<string,map<string,int>>
代替,或者用unordered_map<string,unordered_map<string,double>>
代替(內部不用排序,效率更高)。
如何防止冗餘:
之前用bool visited[]
判斷一個點有沒有搜索過,現在用set<string>
即可。
class Solution {
public:
unordered_map<string,unordered_map<string,double>> graph;
vector<double> ans;
vector<double> calcEquation(vector<vector<string>>& equations, vector<double>& values, vector<vector<string>>& queries) {
for(int i=0;i<equations.size();i++){
auto &edge = equations[i];
graph[edge[0]][edge[1]] = values[i];
graph[edge[1]][edge[0]] = 1/values[i];
}
for(auto query:queries) {
unordered_set<string> vis;
if(!graph.count(query[0]) || !graph.count(query[1]) ){
ans.push_back(-1);
continue;
}
double res = 1;
if(!dfs(query[0],query[1],res,vis)){
ans.push_back(-1);
continue;
}
ans.push_back(res);
}
return ans;
}
// a是搜索的起點,b是目標點
bool dfs(string a,string b,double &res,unordered_set<string> vis){
if(a==b){
return true;
}
vis.insert(a);
unordered_map<string,double> & ma = graph[a];
for(unordered_map<string,double>::iterator it=ma.begin();it!=ma.end();it++){
if(!vis.count(it->first)){
res*= it->second;
if(dfs(it->first,b,res,vis)){
return true;
}
res/= it->second;
vis.erase(it->first);
}
}
return false;
}
};
並查集(未路徑壓縮)
這裏並沒有進行路徑壓縮,嚴格來講查詢的效率降低了很多。
class Solution {
public:
unordered_map<string,string> father;
unordered_map<string,double> weights;
vector<double> calcEquation(vector<vector<string>>& equations, vector<double>& values, vector<vector<string>>& queries) {
vector<double> ans;
for(int i=0;i<equations.size();i++){
auto &equation = equations[i];
string a = equation[0];
string b = equation[1];
if(!father.count(a)){
father[a] = a;
weights[a] = 1;
}
if(!father.count(b)){
father[b] = b;
weights[b] = 1;
}
merge(a,b,values[i]);
}
for(auto &query:queries){
string a = query[0];
string b = query[1];
if(!father.count(a) || !father.count(b)){
ans.push_back(-1);
continue;
}
auto pa = find(a);
auto pb = find(b);
if(pa.first!=pb.first){
ans.push_back(-1); //不連通
continue;
}
// a/b = (a/r) / (b/r)
ans.push_back(pa.second/pb.second);
}
return ans;
}
pair<string,double> find(string a){
double res = 1;
while(a!=father[a]){
res*=weights[a];
a = father[a];
}
return make_pair(a,res);
}
// ar/br = (b/br) / (a/ar) * (a/b)
void merge(string a,string b,double a_b){
auto pa = find(a);
auto pb = find(b);
father[pa.first] = pb.first;
weights[pa.first] = pb.second/pa.second*a_b;
}
};
並查集(路徑壓縮)
那如果要路徑壓縮呢?
把find
函數改成這樣既可。
pair<string,double> find(string a){
if(a==father[a]){
return make_pair(a,1);
}
auto pa_father = find(father[a]);
father[a] = pa_father.first;
weights[a] = pa_father.second*weights[a];
return make_pair(father[a],weights[a]);
}
完整代碼:
class Solution {
public:
unordered_map<string,string> father;
unordered_map<string,double> weights;
vector<double> calcEquation(vector<vector<string>>& equations, vector<double>& values, vector<vector<string>>& queries) {
vector<double> ans;
for(int i=0;i<equations.size();i++){
auto &equation = equations[i];
string a = equation[0];
string b = equation[1];
if(!father.count(a)){
father[a] = a;
weights[a] = 1;
}
if(!father.count(b)){
father[b] = b;
weights[b] = 1;
}
merge(a,b,values[i]);
}
for(auto &query:queries){
string a = query[0];
string b = query[1];
if(!father.count(a) || !father.count(b)){
ans.push_back(-1);
continue;
}
auto pa = find(a);
auto pb = find(b);
if(pa.first!=pb.first){
ans.push_back(-1); //不連通
continue;
}
// a/b = (a/r) / (b/r)
ans.push_back(pa.second/pb.second);
}
return ans;
}
pair<string,double> find(string a){
if(a==father[a]){
return make_pair(a,1);
}
auto pa_father = find(father[a]);
father[a] = pa_father.first;
weights[a] = pa_father.second*weights[a];
return make_pair(father[a],weights[a]);
}
// ar/br = (b/br) / (a/ar) * (a/b)
void merge(string a,string b,double a_b){
auto pa = find(a);
auto pb = find(b);
father[pa.first] = pb.first;
weights[pa.first] = pb.second/pa.second*a_b;
}
};