zoukankan      html  css  js  c++  java
  • leetcode 76 dp& 强连通分量&并查集经典操作

    800. Similar RGB Color##

    class Solution {
        int getn(int k){
            return (k+8)/17;
        }
        string strd(int k){
            char ret[3];
            if(k<=9){
                ret[1]=char(k+'0');
                ret[0]=char(k+'0');
            }
            else{
               ret[0]=char(k-10+'a');
               ret[1]=char(k-10+'a');
            }
            ret[2]='';
            return string(ret);
        }
    public:
        string similarRGB(string color) {
            string a=string("#");
            for(int i=1;i<=5;i+=2){
                int y=getn(strtoul(color.substr(i,2).c_str(),0,16));
                // printf("%d
    ",y);
                a+=strd(y);
            }
            return a;
        }
    };
    

    以上是我的解法,网上搜了一下rbg string 转int的方式
    归一的时候使用了四舍五入的技巧

    801. Minimum Swaps To Make Sequences Increasing##

    动态规划
    在每一个位置分别记录两个状态 ,在这个 位置换 和在这个位置不换得到的最小操作数(当然前提是可以)

    考虑倒 上一个位置就有两种可能, 那么一共有四种递推可能,地推方程略

    class Solution {
    public:
        int minSwap(vector<int>& A, vector<int>& B) {
            const int maxn=200000;
            int len=A.size();
            int t=1,n=0;
            for(int i=1;i<len;i++){
                int pt=t,pn=n;
                int tt=0,nn=0,tn=0,nt=0;
                if(A[i]>A[i-1]&&B[i]>B[i-1]){
                    tt=1;
                    nn=1;
                }
                if(A[i]>B[i-1]&&B[i]>A[i-1]){
                    tn=1;
                    nt=1;
                }
          
                n=min(tn?(pt):maxn,nn?(pn):maxn);
                t=min((tt)?(pt+1):(maxn),(nt)?(pn+1):(maxn));
            }
            return min(t,n);
        }
    };
    

    下面是一个简介的py 版本大意相同

    def minSwap(self, A, B):
        """
        :type A: List[int]
        :type B: List[int]
        :rtype: int
        """
        n = len(A)
        pre = [0, 1]
        for i in range(1, n):
            cur = [sys.maxsize, sys.maxsize]
            if A[i]>A[i-1] and B[i]>B[i-1]:
                cur[0] = min(cur[0], pre[0])
                cur[1] = min(cur[1], pre[1]+1)
            if A[i]>B[i-1] and B[i]>A[i-1]:
                cur[0] = min(cur[0], pre[1])
                cur[1] = min(cur[1], pre[0]+1)
            pre = cur
            return min(pre)
    

    802. Find Eventual Safe States##

    tarjan 算法的变形。
    总而言之,我们定义一个bad 的连通分量: 如果他的 规模大于1 或者有子环或者通向其他bad的连通分量则为bad 联通分量 ,问非bad的联通分量有多少个

    在经典的tarjan 算法中,判断一个下一个节点是否是自己(标机bad)
    遍历到 没有vis 但是dfn 确有的节点时要注意( 因为一次深搜有可能无法解决所有的节点) 我们需要选择多个入口,就出现了这种问题
    然后发现一个强连通分量的时候判断规模 是否大于1个 如果大于 那么这个强连通分量就标记为bad

    下面是代码

    class Solution {
        int cnt,top;
        int dfn[10002];
        int low[10002];
        int stack[10002];
        int bad[10002];
        int vis[10002];
        
        int dfs(int k,vector<vector<int> >&graph2){
            if(vis[k])return low[k];
            dfn[k]=++cnt;        //时间戳
            low[k]=dfn[k];       //扩展戳
            stack[top++]=k;      //栈中节点
            vis[k]=1;            //栈中标记
            int len=graph2[k].size();
            for(int i=0;i<len;i++){
                if(graph2[k][i]==k)bad[k]=1;//子环标志
                if(!vis[graph2[k][i]]&&dfn[graph2[k][i]]){ //如果没有vis栈上没有,又已经被访问过了,只有一种可能 多次沿不同路径的访问
                    if(bad[graph2[k][i]])bad[k]=1;
                    continue;
                }
                low[k]=min(low[k],dfs(graph2[k][i],graph2));
                if(bad[graph2[k][i]])bad[k]=1;//通向bad;
            }
            
            if(low[k]==dfn[k]){ //发现一个强连通分量 目标是找到所有的 只能通往非bad且自身也非bad的强连通分量 
                                // bad 1 存在子环或者规模大于1或者可以通向bad
                if(stack[top-1]!=k){
                    while(stack[top-1]!=k){
                        vis[stack[--top]]=0;
                        bad[stack[top]]=1;
                    }
                    vis[stack[--top]]=0;
                    bad[stack[top]]=1;
                    
                }else{
                        while(stack[top-1]!=k){
                        vis[stack[--top]]=0;
                    }
                    vis[stack[--top]]=0;
                }
            }
            return low[k];
        }
    public:
        vector<int> eventualSafeNodes(vector<vector<int> >& graph) {
            vector<int> ans;
            int len=graph.size();
            cnt=0,top=0;
            ans.clear();
            memset(dfn,0,sizeof(dfn));
            memset(low,0,sizeof(low));
            memset(bad,0,sizeof(bad));
            memset(vis,0,sizeof(vis));
            for(int i=0;i<len;i++){
                if(!dfn[i])dfs(i,graph);
            }
            for(int i=0;i<len;i++){
                if(dfn[i]==low[i]&&!bad[i])ans.push_back(i);
            }
            return ans;
        }
    };
    

    官方Solution
    先找到没有出度的点 标记为safe
    然后一次处理这些safe 的点 从反向图中找到这些safe 点的上家,如果上家

    class Solution(object):
        def eventualSafeNodes(self, graph):
            N = len(graph)
            safe = [False] * N
    
            graph = map(set, graph)     #从list 模式转换到set 模式
                         
            rgraph = [set() for _ in xrange(N)]  为反向图创建 list of set
    
            q = collections.deque()  处理队列
    
            for i, js in enumerate(graph):
                if not js:
                    q.append(i)  #没有出度的节点先进入队列 等待处理
                for j in js:
                    rgraph[j].add(i) #创建反向图
    
            while q:
                j = q.popleft()  # 取出一个安全的节点
                safe[j] = True  # 标记为safe 
                for i in rgraph[j]: #找到他所有的上家
                    graph[i].remove(j) #在正常图中移除 上家连接他的关系 
                    if len(graph[i]) == 0: #如果上家移除了之后没有其余下家了 就认为上家同样安全
                        q.append(i)
    
            return [i for i, v in enumerate(safe) if v]
    

    这个思路简介明了 直奔主题, 确实是好思路

    官方Solution2 暴力深搜
    entry 标记为gray
    exit 就标记为black 没有访问就是white
    实际上这个流程非常实用,我们进行少许的修改
    我们认为如果一个节点的出节点 连接了任意那怕一个gray我们就认为他是gray,反之则标记为gray

    class Solution(object):
        def eventualSafeNodes(self, graph):
            WHITE, GRAY, BLACK = 0, 1, 2
            color = collections.defaultdict(int)
    
            def dfs(node):
                if color[node] != white:
                    return color[node] == BLACK
    
                color[node] = GRAY
                for nei in graph[node]:
                    if color[nei] == BLACK:
                        continue
                    if color[nei] == GRAY or not dfs(nei):
                        return False
                color[node] = BLACK
                return True
    
            return filter(dfs, range(len(graph)))
    

    803. Bricks Falling When Hit##

    受过启发的naive 思路先把所有的cut 点都去掉 然后做一遍深搜 将safe的点标记出来
    从尾到头依次加入cut 点,每加入一个cut 就在那个cut周围做一次dfs 但是bug 是明显的

    官方Solution
    倒叙加入利用并查集的实现
    并查集的经典操作

    class DSU:
        def __init__(self, R, C):
            #R * C is the source, and isn't a grid square
            self.par = range(R*C + 1)
            self.rnk = [0] * (R*C + 1)
            self.sz = [1] * (R*C + 1)
    
        def find(self, x):         #经典的并查集操作, 
        # if par[x] !=x 
        # par[x]=find(par[x])
        # return par[x] 
            if self.par[x] != x:
                self.par[x] = self.find(self.par[x])
            return self.par[x]
    
        def union(self, x, y):
            xr, yr = self.find(x), self.find(y)
            if xr == yr: return
            if self.rnk[xr] < self.rnk[yr]:
                xr, yr = yr, xr
            if self.rnk[xr] == self.rnk[yr]:
                self.rnk[xr] += 1
            ## 保证rnk[xr] 稍大一点
            self.par[yr] = xr
            self.sz[xr] += self.sz[yr]
    
        def size(self, x):
            return self.sz[self.find(x)]
    
        def top(self):
            # Size of component at ephemeral "source" node at index R*C,
            # minus 1 to not count the source itself in the size
            return self.size(len(self.sz) - 1) - 1
    
    class Solution(object):
        def hitBricks(self, grid, hits):
            R, C = len(grid), len(grid[0])  #gird的行和 列
            def index(r, c):                       #索引 index的函数
                return r * C + c
    
            def neighbors(r, c):                #神奇的操作,生成邻居器
                for nr, nc in ((r-1, c), (r+1, c), (r, c-1), (r, c+1)):
                    if 0 <= nr < R and 0 <= nc < C:
                        yield nr, nc
    
            A = [row[:] for row in grid] 我感觉,就是grid的拷贝???
            for i, j in hits:                  #将相应的cut 置零
                A[i][j] = 0
    
            dsu = DSU(R, C)
    
            for r, row in enumerate(A):
                for c, val in enumerate(row):         #枚举 处理后图 的节点 
                    if val:                                          #如果为1
                        i = index(r, c)                         
                        if r == 0:
                            dsu.union(i, R*C)               #如果是第一行,就union 到假想节点RC
                        if r and A[r-1][c]:                      
                            dsu.union(i, index(r-1, c))  #如果他的下侧有值 ,就union 到一起
                        if c and A[r][c-1]:
                            dsu.union(i, index(r, c-1))  #如果他的上侧有值 , 就union 到一起
    
            ans = []
            for r, c in reversed(hits):                     #倒叙 枚举 可算找到了,倒叙枚举用reversed 
                pre_roof = dsu.top()                       #查照总共有多少个节点
                if grid[r][c] == 0:                              #如果在原图中就是空  直接就是0好了
                    ans.append(0)
                else:                                                                               
                    i = index(r, c)
                    for nr, nc in neighbors(r, c):         
                        if A[nr][nc]:
                            dsu.union(i, index(nr, nc))   #枚举所有的邻居,和邻居union
                    if r == 0:
                        dsu.union(i, R*C)                    #如果是roof 节点,还要和假想节点union 
                    A[r][c] = 1
                    ans.append(max(0, dsu.top() - pre_roof - 1)) #最后输出前后的差异
            return ans[::-1]
    
  • 相关阅读:
    性能
    .Net 平台下的互联网架构新思考
    bootstrap-paginator 分页插件笔记
    HTTP 报文中的 Header 字段进行身份验证
    .NET简单企业应用
    djngo快速实现--使用Bootstrap
    Knockout应用开发指南
    Linux下OpenCV的环境搭建(转)
    初识树莓派(转)
    网络名词解释
  • 原文地址:https://www.cnblogs.com/sfzyk/p/8603290.html
Copyright © 2011-2022 走看看