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]='\0';
        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\n",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]