力扣第299场周赛补题

T1:判断矩阵是否是一个X矩阵

题链

思路:正反对角线在i=j和i+j=n-1两条线上,直接遍历所有点即可。

class Solution {
public:
    bool checkXMatrix(vector<vector<int>>& grid) {
        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]) return false;
                }else{
                    if(grid[i][j]) return false;
                }
        return true;
    }
};

T2: 统计放置房子的方式数

题链

思路:用0表示该位未放置房子,用1表示该位放置房子,把问题转换为不存在连续两个1的01子序列的方案数(状态机问题:一维表示路径长度,二维表示哪种状态)

class Solution {
public:
    int countHousePlacements(int n) {
        vector<vector<int>> f(n+1,vector<int>(2));
        const int MOD=1e9+7;
        f[1][0]=f[1][1]=1;
        for(int i=2;i<=n;i++){
            f[i][0]=(f[i-1][1]+f[i-1][0])%MOD;//状态0可以由1和0转移来
            f[i][1]=f[i-1][0];//状态1只能由0转移来
        }
        long long res=f[n][0]+f[n][1];//long long 防止下面乘积爆int
        return res*res%MOD;
    }
};

或者发现其实际上是斐波那契数列的规律

class Solution {
public:
    int countHousePlacements(int n) {
        int a=1,b=1,MOD=1e9+7;
        int c;
        while(--n){
            c=(a+b)%MOD;
            a=b;
            b=c;
        }
        a+=b;
        return (long long)a*a%MOD;
    }
};

T3:拼接数组的最大分数

题链

思路:比赛时思路,找nums1中对位替换后对nums2之和增大贡献最大的连续子序列,然后再找nums2中对位替换后对nums1之和增大贡献最大的连续子序列,取两值较大的。

class Solution {
public:
    int maximumsSplicedArray(vector<int>& nums1, vector<int>& nums2) {
        int n=nums1.size();
        int sum1=0,sum2=0;
        for(auto i:nums1) sum1+=i;
        for(auto i:nums2) sum2+=i;
            //从nums2中找替换子序列
            int j=0,sum=0,res=0;
            for(int j=0;j<n;j++){
                while(j<n&&sum>=0){
                    sum+=nums2[j]-nums1[j];//替换后对和的贡献
                    j++;
                    res=max(res,sum);//子序列每添加一个数更新一次最大贡献
                }
                sum=0;//当sum<0后,清空贡献,从当前位重新开始找替换子序列
                j--;
            } 
            res+=sum1;//贡献与原数组之和相加
            //从nums1中找替换子序列
            int i=0,num=0,ans=0;
            for(int i=0;i<n;i++){
                while(i<n&&num>=0){
                    num+=nums1[i]-nums2[i];
                    i++;
                    ans=max(ans,num);
                }
                num=0;
                i--;
            }
            ans+=sum2;
 
        int ans1=max(ans,res);
        return ans1;
    }
};

代码简化:

class Solution {
public:
    int replace(vector<int>& a,vector<int>& b){
        int sum=0;
        for(auto x:a) sum+=x;
        int dt=0,f=0;
        for(int i=0;i<a.size();i++){
            f=max(f,0)+b[i]-a[i];
            dt=max(dt,f);
        }
        return sum+dt;
    }
    int maximumsSplicedArray(vector<int>& nums1, vector<int>& nums2) {
        return max(replace(nums1,nums2),replace(nums2,nums1));
    }
};

T4:从树中删除边的最小分数

题链

思路:先删除一条边,再选择删除第二条边(枚举+图的遍历)

const int N=1010,M=N*2;
int h[N],e[M],ne[M],idx;//邻接表
class Solution {
public:
    int ans=1e9;
    vector<int> w;
    void add(int a,int b){
        e[idx]=b;//存该点的值
        ne[idx]=h[a];//存该点的上一个节点的索引
        h[a]=idx++;//存与点a距离为1的点的索引
    }
    int dfs(int u,int fa,int sumx,int sumy){
        int res=w[u];//当前节点的权值
        for(int i=h[u];i!=-1;i=ne[i]){//遍历与节点u的邻边
            int j=e[i];
            if(j==fa) continue;//等于父节点则跳过 保证直往下搜索
            int t=dfs(j,u,sumx,sumy);
            res^=t;
            if(sumx!=-1){
                int a[3]={sumy,t,sumx^t};//先分出两部分x、y,x部分再分出t,现在x部分异或和为sumx^t
                //举例 原本x部分的节点为1 2 3 4 5  sumx=1^2^3^4^5
                //分出3 4   现t部分异或和为t=3^4   sumx=1^2^3^4^5^t=1^2^(3^3)^(4^4)^5=1^2^3
                sort(a,a+3);
                ans=min(ans,a[2]-a[0]);//求最小差值
            }
        }
        return res;
    }
    int minimumScore(vector<int>& nums, vector<vector<int>>& edges) {
        int n=nums.size();
        w=nums;
        for(int i=0;i<n-1;i++){//枚举n-1条边
            memset(h,-1,n*4);//只需要n个,一个int4个字节,不用全部初始化
            idx=0;
            for(int j=0;j<n-1;j++){
                if(i!=j){
                    int a=edges[j][0],b=edges[j][1];
                    add(a,b),add(b,a);//建图
                }
            }
            int x=edges[i][0],y=edges[i][1];//删除第i条边
            int sumx=dfs(x,-1,-1,-1),sumy=dfs(y,-1,-1,-1);//删除第i条边后两子树异或和
            dfs(x,-1,sumx,sumy);//选择从x部分删除第2个条边还是从y部分删除第2条边
            dfs(y,-1,sumy,sumx);
        }
        return ans;
    }
};

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值