图论——leetcode

851. 喧闹和富有 - 力扣(LeetCode)

复习一下链式前向星
注意题目要求的是拓扑序严格大于的数,这个时候按顺序统计的拓扑序并不是严格大于的关系,因此要在拓扑图中不断更新

class Solution {
    const static int N = 510 * 510;
public:
    int ne[N], idx, h[N], e[N], ind[N];
    void add(int a, int b)
    {
        e[idx] = b, ne[idx] = h[a], h[a] = idx ++; 
    }
    vector<int> loudAndRich(vector<vector<int>>& richer, vector<int>& quiet) {
        //更有钱且最安静的人
        //寻找拓扑序,然后找到更安静的人
        //b ---> a
        idx = 0;
        memset(h, -1, sizeof h);
        int n = quiet.size();
        for(int i = 0; i < richer.size(); i ++ )
        {
            int a = richer[i][0], b = richer[i][1];
            add(a, b);//a到b的一条边
            ind[b] ++;
        }
        vector<int> ans(n);
        queue<int> q;
        for(int i = 0; i < n; i ++ )
        {
            if(!ind[i]) q.push(i);
            ans[i] = i;//初始答案
        }
        while(q.size())
        {
            int u = q.front();
            q.pop();
            for(int i = h[u]; i != -1; i = ne[i])
            {
                int j = e[i];//u比j更有钱
                ind[j] --;
                if(quiet[ans[j]] > quiet[ans[u]])//u更加安静
                    ans[j] = ans[u];
                if(!ind[j]) q.push(j);
            }
        }
        return ans;
    }
};

搜索版本,对每个点搜索拓扑序比他大的点,更新最小quiet

class Solution {
public:
    vector<int> loudAndRich(vector<vector<int>> &richer, vector<int> &quiet) {
        int n = quiet.size();
        vector<vector<int>> g(n);
        for (auto &r : richer) {
            g[r[1]].emplace_back(r[0]);
        }
        vector<int> ans(n, -1);
        function<void(int)> dfs = [&](int x) {
            if (ans[x] != -1) {
                return;
            }
            ans[x] = x;
            for (int y : g[x]) {
                dfs(y);
                if (quiet[ans[y]] < quiet[ans[x]]) {
                    ans[x] = ans[y];
                }
            }
        };
        for (int i = 0; i < n; ++i) {
            dfs(i);
        }
        return ans;
    }
};

310. 最小高度树 - 力扣(LeetCode)

对于无向图,如何进行拓扑排序,可以设置入度最小为1,这时拓扑序是外层小于内层。

class Solution {
public:
    vector<int> findMinHeightTrees(int n, vector<vector<int>>& edges) {
        //最大值最小
        //删除叶子节点,最终会剩下一个点或者两个点,如果有第三个点,那么可以删除
        vector<int> g[n];
        vector<int> ind(n);
        for(int i = 0; i < n - 1; i ++ )
        {
            g[edges[i][0]].push_back(edges[i][1]);
            g[edges[i][1]].push_back(edges[i][0]);
            ind[edges[i][0]] ++;
            ind[edges[i][1]] ++;
        }
        queue<int> q;
        vector<bool> st(n);
        vector<int> dist(n);
        int cnt = 0;
        for(int i = 0; i < n; i ++ )
        {
            if(ind[i] == 1)
            {
                q.push(i); 
                dist[i] = 0; 
            }
        }
        while(n - (cnt ++) > 2)//最终会剩余两个或者一个,这时到达了最内层
        {
            int u = q.front();
            q.pop();
            st[u] = true; // 删除
            for(int i = 0; i < g[u].size(); i ++ )
            {
                int j = g[u][i];
                if(!st[j])//为了防止反复,经过的不会再经过
                {
                    dist[j] = max(dist[j], dist[u] + 1);
                    if(-- ind[j] == 1) q.push(j);
                }
            }
        }
        int res = *max_element(dist.begin(), dist.end());
        vector<int> ans;
        for(int i = 0; i < n; i ++ )
            if(dist[i] == res) ans.push_back(i);
        return ans;
    }
};

802. 找到最终的安全状态 - 力扣(LeetCode)

反向建图 + 拓扑排序
一个节点不会进入环则是安全的,正向无法保证这个点的入度为0,反向建图,从终点开始拓扑排序,找到所有满足要求的点

class Solution {
public:
    vector<int> eventualSafeNodes(vector<vector<int>>& graph) {
        //找到拓扑图的所有点
        int n = graph.size();
        vector<int> ind(n);
        vector<int> g[n];
        for(int i = 0; i < n; i ++ )
        {
            for(int j = 0; j < graph[i].size(); j ++ )
            {
                ind[i] ++;
                g[graph[i][j]].push_back(i);//反向边
            }
        }
        vector<int> ans;
        queue<int> q;
        for(int i = 0; i < n; i ++ )
        {
            if(!ind[i])
            {
                q.push(i);
            }   
        }
        while(q.size())
        {
            auto u = q.front();
            q.pop();
            ans.push_back(u);
            for(int i = 0; i < g[u].size(); i ++ )
            {
                int j = g[u][i];
                ind[j] --;
                if(!ind[j]) q.push(j);
             }
        }
        sort(ans.begin(), ans.end());
        return ans;
    }
};

407. 接雨水 II - 力扣(LeetCode)

该点的盛的雨水取决于周围边界的最小值,最短路的思路,从外圈最小高度不断扩展,

class Solution {
public: 
    typedef pair<int, pair<int,int>> PIII;
    priority_queue<PIII, vector<PIII>, greater<PIII>> q;
    bool st[201][201];
    int dx[4] = {0, 0, 1, -1};
    int dy[4] = {1, -1, 0, 0};
    int trapRainWater(vector<vector<int>>& heightMap) {
        int n = heightMap.size(), m = heightMap[0].size();
        int ans = 0;
        for(int i = 0; i < n; i ++ ){
            for(int j = 0; j < m; j ++ ){
                if(i == 0 || i == n - 1 || j == 0 || j == m - 1){
                    q.push({heightMap[i][j], {i, j}});
                    st[i][j] = true;
                }
            }
        }
        while(q.size()){
            auto t = q.top();
            int h = t.first, x = t.second.first, y = t.second.second;
            q.pop();
            for(int i = 0; i < 4; i ++ ){
                int nx = dx[i] + x, ny = dy[i] + y;
                if(nx < 0 || ny < 0 || nx >= n || ny >= m || st[nx][ny]) continue;
                if(heightMap[nx][ny] < h){
                    ans += h - heightMap[nx][ny];
                }
                st[nx][ny] = true;
                q.push({max(heightMap[nx][ny], h), {nx, ny}});
            }
        }
        return ans;
    }
};

并查集的思路:
由于数值的范围在20000以内,可以用并查集搞。
把要流出去的点和n * m相连。从0到最高点,依次考虑每个高度。
假设当前考虑到高度为v的方块,枚举所有高度为v的方块,计算有多少个v的块可以填一个高度的水。
由于所以低于这个方块的都已经考虑过,直接看周围是否有已经vis过的方块,然后把他们和当前块连起来,表示往当前块上放水会流向这些块。
对于这一层的答案,就是高度为v的块 - 要流出去的块。
详细来说 对于给定高度v的块,我们给遍历到的全部格子加上v+1的水量,因为高于v的格子不会有水的增加,检查四周的格子是否遍历过,如果这个格子已经是边界,那么表示增加的水会流出来,如果四周是遍历过表示水会从这个格子流向四周这些格子,如果这些格子有流向边界的,那么这些格子的水都会流出边界。最终对于每个水位增加的水量为cnt - sz[find(n*m)],这是遍历到的格子增加水量,但是有的格子会从边界流出,边界点为n * m

class Solution {
public:
    int f[220 * 220], vis[220 * 220], sz[220 * 220];
    int dx[4] = {0, 0, 1, -1};
    int dy[4] = {1, -1, 0, 0};
    int n, m;
    int find(int x )
    {
        if(f[x] == x) return x;
        return f[x] = find(f[x]);
    }
    bool check(int x, int y)
    {
        if(x < 0 || x >= n || y < 0 || y >= m) return 0;
        return 1;
    }
    int trapRainWater(vector<vector<int>>& heightMap) {
        n = heightMap.size(), m = heightMap[0].size();
        for(int i = 0; i <= n * m; i ++ ) f[i] = i, sz[i] = 1;
        sz[n * m] = 0;
        int V = 0;
        vector<int> hp[20010];
        for(int i = 0; i < n; i ++ )
        for(int j = 0; j < m; j ++ )
        {
            hp[heightMap[i][j]].push_back(i * m + j);
            V = max(heightMap[i][j], V);
        }
        int cnt = 0, res = 0;
        for(int v = 0; v < V; v ++ )
        {  
            for(int i = 0; i < hp[v].size(); i ++ )
            {
                cnt ++;
                int x = hp[v][i] / m, y = hp[v][i] % m;
                vis[x * m + y] = 1;
                for(int k = 0; k < 4; k ++ )
                {
                    int nx = x + dx[k], ny = y + dy[k];
                    int flag = 0;
                    if(!check(nx, ny) || vis[nx * m + ny]) //四周有处理过或者是边界
                    {
                        int fx, fy;
                        if(!check(nx, ny)) fx = find(n * m);
                        else fx = find(nx * m + ny);
                        fy = find(x * m + y);
                        if(fx == fy) continue;
                        sz[fx] += sz[fy];
                        f[fy] = fx;
                    }
                }
            }
            cout << cnt << " " << sz[find(n * m)] << endl;
            res += cnt - sz[find(n * m)];
        }
        return res;
    }
};

1631. 最小体力消耗路径 - 力扣(LeetCode)

我使用了prim算法,这样不用建立并查集,但是因为不是稠密图,所以效率不高

class Solution {
public:
    int dist[10010];
    int st[10010];
    int dx[4] = {0, 0, 1, -1}, dy[4] = {1, -1, 0, 0};
    int minimumEffortPath(vector<vector<int>>& heights) {
        //找到一条路径,它的高度差最大值最小
        //二分答案,找到合适的dfs路径
        //对于所有的边,从小到大,并查集直到两个点相连。
        //从左上角开始,每次寻找离自己这个集合最近的一个点,
        int n = heights.size(), m = heights[0].size();
        int ans = 0;
        memset(dist, 0x3f, sizeof(dist));
        dist[0] = 0;
        for(int i = 1; i <= n * m; i ++ )//最多需要加入nm个点
        {
            int t = -1;
            for(int j = 0; j < n * m; j ++ )
            {
                if(!st[j] && (t == -1 || dist[t] > dist[j]))
                    t = j;
            }
            ans = max(ans, dist[t]);//记录所有加入的点的最大路径值
            st[t] = true;
            if(t == n * m - 1) break;
            int x = t / m, y = t % m;//这个点是要加入的点
            for(int j = 0; j < 4; j ++ )//更新一下周围的点
            {
                int nx = x + dx[j], ny = y + dy[j];
                if(nx < 0 || ny < 0 || nx >= n || ny >= m) continue;
                dist[nx * m + ny] = min(dist[nx * m + ny], abs(heights[nx][ny] - heights[x][y]));
            }   
        }
        return ans;
    }
};

778. 水位上升的泳池中游泳 - 力扣(LeetCode)

堆+广度优先搜索,从最小的dep寻找是否能到达四周的游泳池,类似于dijkstra算法

class Solution {
public:
    typedef pair<int,int> PII;
    int dx[4] = {0, 0, 1, -1};
    int dy[4] = {1, -1, 0, 0};
    int swimInWater(vector<vector<int>>& grid) {
       //深度优先搜索
        int n = grid.size(), m = grid[0].size();// x * m + y
        priority_queue <PII, vector<PII>, greater<PII> > q;
        vector<int> st(m * n);
        q.push({grid[0][0], 0});
        st[0] = true;
        int ans;
        int cnt = 0;
        while(!q.empty())
        {
            cnt ++;
            auto t = q.top();
            q.pop();
            int dep = t.first, u = t.second;
            if(u == m * n - 1)
            {
                ans = dep;
                break;
            }
            for(int i = 0; i < 4; i ++ )
            {
                int nx = dx[i] + u / m;
                int ny = dy[i] + u % m;
                if(nx < 0 || ny < 0 || nx >= n || ny >= m) continue;
                if(st[nx * m + ny]) continue;//只会入队一次
                q.push({max(dep, grid[nx][ny]), nx * m + ny});
                st[nx * m + ny] = true;
            }
        }
        return ans;
    }
};

从小到大,如果四周有比它小的那就是可以游到,知道可以游到终点。

class Solution {
public:
    int f[2510];
    int dx[4] = {0, 0, 1, -1};
    int dy[4] = {1, -1, 0, 0};
    int find(int x)
    {
        if(f[x] == x) return x;
        return f[x] = find(f[x]);
    }
    int swimInWater(vector<vector<int>>& grid) {
        //从最小的开始,每次将这个点和周围比他小的点连接起来,直到起点和终点连接起来了
        vector<pair<int, int>> nums;
        int n = grid.size();
        for(int i = 0; i < n * n; i ++ ) f[i] = i;
        for(int i = 0; i < n; i ++ )
        for(int j = 0; j < n; j ++ )
        {
            nums.push_back({grid[i][j], i * n + j});
        }
        sort(nums.begin(), nums.end());//从小到大
        int ans;
        for(int i = 0; i < nums.size(); i ++ )
        {
            int dep = nums[i].first, u = nums[i].second;
            int a = find(u);
            for(int j = 0; j < 4; j ++ )
            {
                int nx = dx[j] + u / n;
                int ny = dy[j] + u % n;
                if(nx < 0 || ny < 0 || nx >= n || ny >= n) continue;
                if(dep > grid[nx][ny])
                {
                    int b = find(nx * n + ny);
                    f[b] = a; 
                }
            }
            if(find(0) == find(n * n - 1))
            {
                ans = dep;
                break;
            }
        }
        return ans;
    }
};

743. 网络延迟时间 - 力扣(LeetCode)

最短路问题

class Solution {
public:
    int dist[110][110];
    int networkDelayTime(vector<vector<int>>& times, int n, int k) {
        //k -- 所有
        memset(dist, 0x3f, sizeof(dist));
        for(int i = 0; i < times.size(); i ++)
        {
            int u = times[i][0], to = times[i][1], w = times[i][2];
            dist[u][to] = w;
        }
        for(int k = 1; k <= n; k ++ )
        for(int i = 1; i <= n; i ++ )
        for(int j = 1; j <= n; j ++ )
        {
            dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
        }
        int ans = 0;
        dist[k][k] = 0;
        for(int i = 1; i <= n; i ++ )
        {
            ans = max(ans, dist[k][i]);
        }
        if(ans == 0x3f3f3f3f) return -1;
        return ans;
    }
};

787. K 站中转内最便宜的航班 - 力扣(LeetCode)

裂开解法
可知Floyd算法是经过中转以后的最短路求解。那么可以算出1、3、7、15、31等次中转的最短路,这样得到k次的最短路就是答案。
注意点:

  • 中转一次的答案不能直接更新到a上,需要一个中间数组temp。
  • 每次dp2记录的是更新(2n + 1)次中转的结果,也就是0,1,3,7,15,31,所以k&1时还要再算一步
class Solution {
public:
    int dp[200][200];
    int dp2[200][200];
    int n;
    int findCheapestPrice(int _n, vector<vector<int>>& flights, int src, int dst, int K) {
        n = _n;
        for(int i = 0; i < n; i++)
        for(int j = 0; j < n; j++)
        {
            if(i != j)
            dp[i][j] = 1e9;
        }
        for(int i = 0; i < flights.size(); i ++)
        {
            int x = flights[i][0], y = flights[i][1], w = flights[i][2];
            dp[x][y] = min(dp[x][y], w);                   
        }
        memcpy(dp2, dp, sizeof(dp));
        while(K)//快速幂求出经过k次中转的最短路
        {
            if(K & 1) qsm(dp, dp2);
            qsm(dp2, dp2);
            K >>= 1;
        }
        return dp[src][dst] == 1e9 ? -1: dp[src][dst];
    }
    void qsm(int a[][200], int b[][200])//a不知道中转了几次,b不知道中转了几次,总共中转了加起来
    {
        static int temp[200][200];
        memset(temp, 0x3f, sizeof(temp));
        for(int m = 0; m < n; m++)
        for(int i = 0; i < n; i++)
        for(int j = 0; j < n; j++)
        {
            if(a[i][m] + b[m][j] <= a[i][j])//小于等于可以围着自己中转
            {
                temp[i][j] = min(temp[i][j], a[i][m] + b[m][j]); 
            }
        }
        memcpy(a, temp, sizeof(temp));//指定的大小是temp的大小
    }
};

这样算出的结果是所有点对的最短路,但是题目要求只要源节点到目的节点的最短路,这样的话不用枚举起点的终点,只要bellman算法松弛k + 1次即可

class Solution {
public:
    int dist[200][200];
    int findCheapestPrice(int n, vector<vector<int>>& flights, int src, int dst, int k) {
        memset(dist, 0x3f, sizeof dist);
        dist[0][src] = 0;
        for(int i = 1; i <= k + 1; i ++ )
        {
            for(auto &t : flights)
            {
                dist[i][t[1]] = min({dist[i][t[1]], dist[i - 1][t[1]], dist[i - 1][t[0]] + t[2]});
            }
        }
        return dist[k + 1][dst] == 0x3f3f3f3f ? -1 : dist[k + 1][dst];
    }
};

1786. 从第一个节点出发到最后一个节点的受限路径数 - 力扣(LeetCode)

class Solution {
public:
    const int mod = 1e9 + 7;
    typedef pair<int, int> PII;
    int countRestrictedPaths(int n, vector<vector<int>>& edges) {
        vector<vector<PII>> g(n + 1);
        for(auto &e :edges)
        {
            int u = e[0], v = e[1], d = e[2];
            g[u].push_back({v, d});
            g[v].push_back({u, d});
        }
        priority_queue<PII, vector<PII>, greater<PII>> q;
        vector<int> dist(n + 1, INT_MAX);
        dist[n] = 0;
        vector<bool> vis(n + 1, false);
        q.push({0, n});
        while(!q.empty())
        {
            auto [dis, to] = q.top();
            q.pop();
            if(vis[to]) continue;
            vis[to] = true;
            for(auto [x, w] : g[to])
            {
                if(dist[x] > dis + w)
                {
                    dist[x] = dis + w;
                    q.push({dist[x], x});
                }
            }
        }
        vector<int> index(n), dp(n + 1);
        iota(index.begin(), index.end(), 1);
        sort(index.begin(), index.end(), [&dist](auto a, auto b){
            return dist[a] > dist[b];
        });
        dp[1] = 1;
        for(auto & ind : index)
        {
            for(auto & [x, _]: g[ind])
            {
                if(dist[x] < dist[ind]) dp[x] = (dp[x] + dp[ind]) % mod;
            }
        }
        return dp[n];
    }
};

1723. 完成所有工作的最短时间 - 力扣(LeetCode)

class Solution {
public:
    int ans = INT_MAX;
    vector<int> jobs;
    int energy(int k)
    {
        int n = jobs.size();
        vector<int> s(k);
        for(int i = 0; i < n; i ++ )//对于每项工作,找到一个最闲的工人分配
        {
            int kk = 0;
            for(int j = 1; j < k; j ++ )
                if(s[j] < s[kk]) kk = j;
            s[kk] += jobs[i];
        }
        int res = 0;
        for(auto &x : s)
            res = max(x, res);//找到最大工作时间
        ans = min(ans, res);
        return res; //能量越小越好 
    }
    void th(int k)
    {
        int n = jobs.size();
        double T = 10000;//一个很高的温度
        random_shuffle(jobs.begin(), jobs.end());//产生随机的工作分配顺序
        while(T > 1e-4) //降温过程
        {
            T *= 0.92;
            int a = rand() % n, b = rand() % n;
            int ea = energy(k);
            swap(jobs[a], jobs[b]);
            int eb = energy(k);
            int dt = eb - ea;
            if(dt <= 0 || exp(-dt / T) > rand() / RAND_MAX) continue;//以一定概率接受
            swap(jobs[a], jobs[b]);//不接受
        }
    }
    int minimumTimeRequired(vector<int>& _jobs, int k) {
        //每个工人至少分配一个工作
        jobs = _jobs;
        for(int i = 0; i < 7; i ++ )
        th(k);
        return ans;
    }
};

状态压缩

class Solution {
public:
    int minimumTimeRequired(vector<int>& jobs, int k) {
        int n = jobs.size();
        int m = (1 << n);
        vector<int> all (m, 0);
        for (int i = 1; i < m; ++i) {
            all[i] = all[i & (i - 1)] + jobs[__builtin_ctz(i)];
        }
        // all[state] -> all time of state 
        vector<vector<int>> dp(k, vector<int>(m, INT_MAX));
        for (int i = 0; i < m; ++i) {
            dp[0][i] = all[i];
        }
        for (int i = 1; i < k; ++i) {
            for (int j = 0; j < m; ++j) {
                for (int x = j; x; x = (x - 1) & j) {
                    dp[i][j] = min(dp[i][j], max(dp[i - 1][j - x], all[x]));
                }
            }
        }
        return dp[k - 1][m - 1];
    }
};

773. 滑动谜题 - 力扣(LeetCode)

#define x first
#define y second
class Solution {
public:
    string end = "123450";
    int dx[4] = {0, 0, 1, -1}, dy[4] = {1, -1, 0, 0};
    typedef pair<int, string> PIS;
    int f(string s)
    {
        int res = 0;
        for(int i = 0; i < s.size(); i ++ )
        if(s[i] != '0')
        {
            int p = s[i] - '1';
            res += abs(i / 3 - p / 3) + abs(i % 3 - p % 3);
        }
        return res;
    }
    int bfs(string state)
    {
        unordered_map <string, int> dist;
        priority_queue <PIS, vector<PIS>, greater<PIS>> q;

        q.push({f(state), state});//将初始状态入栈
        dist[state] = 0;
        while(q.size())
        {
            auto t = q.top();
            q.pop();
            string s = t.y;
            if(s == end) break;
            int x, y;
            for(int i = 0; i < 6; i ++ )//找到0的位置
            {
                if(s[i] == '0')
                {
                    x = i / 3;
                    y = i % 3;
                    break;
                }
            }
            for(int i = 0; i < 4; i ++ )//和上下左右交换
            {
                int nx = x + dx[i], ny = y + dy[i];
                if(nx < 0 || ny < 0 || nx > 1 || ny > 2) continue;//两行三列
                string source = s;
                swap(source[x * 3 + y], source[nx * 3 + ny]);
                
                if(!dist.count(source) || dist[source] > dist[s] + 1)//dist是无穷大或者是比s + 1大
                {
                    dist[source] = dist[s] + 1;
                    q.push({f(source) + dist[source], source});
                }
            }
        }
        return dist[end];
    }
    int slidingPuzzle(vector<vector<int>>& board) {
        //A* 多次入栈,终点出栈只能确定终点的最短路径
        //判断是否有解
        string start;
        for(auto &t : board)
        for(auto &x : t)
            start += x + '0';
        int cnt = 0;
        for(int i = 0; i < 6; i ++ )
        for(int j = i + 1; j < 6; j ++ )
        {
            if(start[i] != '0' && start[j] != '0' && start[j] < start[i]) cnt ++;
        }
        if(cnt % 2) return -1;
        else return bfs(start);
    }
};

1239. 串联字符串的最大长度 - 力扣(LeetCode)

class Solution {
public:
    vector<string> arr;
    int n;
    int ans = 0;
    bool book[27];
    int enengy()
    {
        int res = 0;
        memset(book, 0, sizeof book);
        for(int i = 0; i < n; i ++ )
        {
            int flag = true;
            for(auto c: arr[i])
            {
                if(book[c - 'a']) 
                {
                    flag = false;
                    break;
                };
                book[c - 'a'] = true;
            }
            if(flag) res += arr[i].size();
        }
        return res;
    }
    void th()
    {
        srand(time(NULL));
        random_shuffle(arr.begin(), arr.end());
        for(int t = 1e6; t > 1e-6; t *= 0.97)
        {
            int a = rand() % n, b = rand() % n;
            int ea = enengy();
            swap(arr[a], arr[b]);
            int eb = enengy();
            ans = max({ans, ea, eb});
            int dt = ea - eb;
            if(dt < 0) continue;
            if(exp(-1 * dt / t) >= rand() / RAND_MAX) continue;
            swap(arr[a], arr[b]);
        }
    }
    int maxLength(vector<string>& _arr) {
        for(auto s: _arr)
        {
            memset(book, 0, sizeof book);
            bool flag = true;
            for(auto c: s)
            {
                if(book[c - 'a']) 
                {
                    flag = false;
                    break;
                }
                book[c - 'a'] = true;
            }
            if(flag) arr.push_back(s);
        }
        n = arr.size();
        if(!n) return 0;
        for(int i = 0; i < 2; i ++ )
        th();
        return ans;
    }
};

你可能感兴趣的:(LeetCode,leetcode,图论,深度优先)