Leetcode第 299 场周赛总结

1.判断矩阵是否是一个 X 矩阵

题目链接
思路:模拟判断即可

bool checkXMatrix(vector<vector<int>>& grid) {
        bool res = true;
        int n = grid.size();
        for(int i = 0; i < n; i ++){
            for(int j = 0; j < n; j ++){
                if(i == j || i + j == n - 1){
                    if(grid[i][j] == 0) 
                        res = false;
                }
                else{
                    if(grid[i][j] != 0)
                        res = false;
                }
                if(!res) return res;
            }
        }
        return res;
    }

2.统计放置房子的方式数

题目链接
思路:状态机DP, 由于道路两边的情况互不影响,因此总方案数为两边的方案数相乘。对于一边的方案数,我们假设 g [ i ] [ 0 ] g[i][0] g[i][0]为第 i i i个位置上不放房子的方案数,则 g [ i ] [ 0 ] = g [ i − 1 ] [ 0 ] + g [ i − 1 ] [ 1 ] , g [ i ] [ 1 ] = g [ i − 1 ] [ 0 ] g[i][0] = g[i - 1][0] + g[i-1][1],g[i][1]=g[i-1][0] g[i][0]=g[i1][0]+g[i1][1],g[i][1]=g[i1][0],那么假设 f [ i ] f[i] f[i]为第 i i i个位置上的总方案数, f [ i ] = g [ i − 1 ] [ 0 ] ∗ 2 + g [ i − 1 ] [ 1 ] f[i] = g[i-1][0]*2+g[i-1][1] f[i]=g[i1][0]2+g[i1][1] f [ i + 1 ] = g [ i ] [ 0 ] ∗ 2 + g [ i ] [ 1 ] = 3 ∗ g [ i − 1 ] [ 0 ] + 2 ∗ g [ i − 1 ] [ 1 ] = f [ i ] + f [ i − 1 ] f[i+1]=g[i][0]*2+g[i][1] =3*g[i-1][0]+2*g[i-1][1]=f[i]+f[i-1] f[i+1]=g[i][0]2+g[i][1]=3g[i1][0]+2g[i1][1]=f[i]+f[i1] f [ i + 1 ] = f [ i ] + f [ i − 1 ] f[i+1]=f[i]+f[i-1] f[i+1]=f[i]+f[i1]是一个斐波那契数列。答案即为 f [ n ] ∗ f [ n ] ( 需 求 余 ) f[n] * f[n](需求余) f[n]f[n]
代码:

class Solution {
public:
    int f[10005];
    static const int mod = 1e9 + 7;
    int countHousePlacements(int n) {
        f[0] = 1, f[1] = 1;
        for(int i = 2; i <= n + 1; i ++) f[i] = (long long)(f[i - 1] + f[i - 2]) % mod;
        int res = (long long)f[n + 1] * f[n + 1] % mod;
        return res;
    }
};

3. 拼接数组的最大分数

题目链接
思路:最大子段和 题目要求从a中选取一段连续字段代替b中一段,或者用b中一段代替a中一段,设置数组 d d d使得 d [ i ] = a [ i ] − b [ i ] d[i]=a[i]-b[i] d[i]=a[i]b[i],计算 d d d数组的最大字段和以及最小字段和,将其 a a a数组的和加上最大字段和即为替换 a a a数组得到的最大值,将 b b b数组的和加上最小字段和的绝对值即为替换 b b b数组的最大值,两者的最大值即为本题的答案。
代码:

class Solution {
public:
    int pd[100005], nd[100005];
    int maximumsSplicedArray(vector<int>& nums1, vector<int>& nums2) {
        int n = nums1.size(), sum1 = 0, sum2 = 0;
        for(int i = 0; i < n; i ++){
            pd[i] = nums1[i] - nums2[i], sum1 += nums1[i], sum2 += nums2[i];
            nd[i] = nums2[i] - nums1[i];
        }
        int tonums2 = 0, tonums1 = 0, x = 0, y = 0;
        for(int i = 0; i < n; i ++){
            x += pd[i], y += nd[i];
            if(x < 0) x = 0;
            if(y < 0) y = 0;
            tonums2 = max(tonums2, x);
            tonums1 = max(tonums1, y);
        }
        return max(tonums1 + sum1, tonums2 + sum2);
    }
};

4.从树中删除边的最小分数

题目链接
思路:首先观察数据范围发现枚举两条删除边的时间复杂度为 1 0 6 10 ^ 6 106,不会超时,所以我们可以采用枚举删除边的方式得到三个连通块。但如果我们注意枚举每个连通块里的值把他们异或起来,则要再乘上 N N N 的时间复杂度,总时间复杂度为 1 0 9 10^9 109,超时了。所以我们可以用一个 d d d 数组代表以该点为根节点其所有子节点的异或值,当要从该树中删去一颗子树时,只需异或上要删去子树的根节点的 d d d值。此时只需分两种情况讨论。
假设 a l l all all为所有节点的异或值,x为第一条删除边的远离根节点的点,y为第二条删除边远离根节点的点。且如果他们在第二种情况时,x比y更靠近根节点

1.两条删除边不在从根节点出发的同一路径上

三块部分的值分别为all^x, x^y, y

2.两条删除边在从根节点出发的同一路径上

三块部分的值分别为all ^ x ^ y, x, y
在这里插入图片描述
那么如何判断x和y是否在同一条到根节点的路径上呢,这里就可以采用时间戳的思想,设置遍历时第一次经过x点的时间为 i n [ x ] in[x] in[x],设置遍历时退出x点的时间为 o u t [ x ] out[x] out[x]。对于dfs序来讲,若一条路径从根节点出发, x点和y点都在其上,且y点在x点下面,则 i n [ x ] < i n [ y ] , o u t [ x ] > o u t [ y ] in[x]<in[y], out[x]>out[y] in[x]<in[y],out[x]>out[y]。这就可以判断x和y是否在同一条从根节点出发的路径上了。
代码:

class Solution {
public:
    static const int N = 2e3 + 5;
    int in[N], out[N], d[N], h[N], ne[N], to[N], idx = 0, timestamp = 0;
    void add(int x, int y){
        ne[idx] = h[x]; to[idx] = y; h[x] = idx ++;
    }
    int dfs(int u, int fa){
        in[u] = timestamp ++;
        for(int i = h[u]; ~i; i = ne[i]){
            int j = to[i];
            if(j == fa) continue;
            d[u] ^= dfs(j, u);
        }
        out[u] = timestamp ++;
        return d[u];
    }
    bool isfa(int x, int y){
        if(in[x] <= in[y] && out[x] >= out[y]) return true;
        return false;
    }
    int minimumScore(vector<int>& nums, vector<vector<int>>& edges) {
        int n = nums.size(), m = edges.size(), all = 0;
        for(int i = 0; i < n; i ++) d[i] = nums[i], all ^= nums[i];
        memset(h, -1, sizeof h);
        for(int i = 0; i < m; i ++){
            add(edges[i][0], edges[i][1]);
            add(edges[i][1], edges[i][0]);
        }
        dfs(0, -1);
        int res = 0x3f3f3f3f;
        int t[4];
        for(int i = 0; i < m; i ++){
            for(int j = i + 1; j < m; j ++){
                int x, y;
                if(in[edges[i][0]] < in[edges[i][1]]) x = edges[i][1];
                else x = edges[i][0];
                if(in[edges[j][0]] < in[edges[j][1]]) y = edges[j][1];
                else y = edges[j][0];
                if(in[x] > in[y]) swap(x, y);
                if(isfa(x, y)){
                    t[0] = all ^ d[x]; t[1] = d[x] ^ d[y]; t[2] = d[y];
                }
                else{
                    t[0] = all ^ d[x] ^ d[y]; t[1] = d[x]; t[2] = d[y];
                }
                sort(t, t + 3);
                res = min(res, t[2] - t[0]);
            }
        }
        return res;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Rockict_z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值