LeetCode - 第207场周赛

Rank

又翻车了…

A. 重新排列单词间的空格

模拟.

class Solution {
public:
    string reorderSpaces(string text) {
        int len=text.size();
        int cnt_word=0,cnt_blank=0;
        vector<string> words;
        for(int i=0;i<len;){
            if(text[i]==' '){
                ++cnt_blank;
                ++i;
                continue;
            }
            cnt_word+=1;
            int j=i+1;
            while(j<len && text[j]!=' ') ++j;
            words.push_back(text.substr(i,j-i));
            i=j;
        }
        string ans="";

        if(cnt_word==1){
            ans+=words[0];
            while(ans.size()<len) ans+=" ";
            return ans;
        }

        cnt_blank/=(cnt_word-1);
        for(int i=0;i<cnt_word;++i){
            ans+=words[i];
            if(i+1<cnt_word){
                for(int j=0;j<cnt_blank;++j) ans+=" ";
            }
        }
        while(ans.size()<len) ans+=" ";

        return ans;
    }
};

B. 拆分字符串使唯一子字符串的数目最大

暴力,每个字符与下一个字符可以拆分,也可以不拆分.

class Solution {
public:
    int len,ans;
    string str;
    vector<string> v;
    
    void dfs(int start,int pos){
        if(pos==len){
            int sz=v.size();
            bool flag=true;
            for(int i=0;i<sz;++i){
                for(int j=i+1;j<sz;++j){
                    if(v[i]==v[j]) flag=false;
                }
            }
            if(flag) ans=max(ans,(int)v.size());
            return;
        }
        dfs(start,pos+1);
        string s=str.substr(start,pos-start+1);
        v.push_back(s);
        dfs(pos+1,pos+1);
        v.pop_back();
    }
    
    int maxUniqueSplit(string s) {
        str=s;
        len=s.length();
        dfs(0,0);
        return ans;
    }
};

C. 矩阵的最大非负积

线性 dp.

dp[i][j][0] 表示从 (0,0) 出发走到(i,j) 的最大非负积,dp[i][j][1] 表示从 (0,0) 出发走到 (i,j) 的最小负数积,然后根据 g[i][j] 的正负号进行分类讨论,最终答案为 dp[n-1][m-1][0],初始状态为 dp[0][0][0]dp[0][0][1].

class Solution {
public:
    int maxProductPath(vector<vector<int>>& grid) {
        int n=grid.size(),m=grid[0].size();
        long long dp[n][m][2];
        for(int i=0;i<n;++i){
            for(int j=0;j<m;++j){
                if(!i && !j){
                    if(grid[i][j]>=0){
                        dp[i][j][0]=grid[i][j];
                        dp[i][j][1]=1;
                    }
                    else{
                        dp[i][j][0]=-1;
                        dp[i][j][1]=grid[i][j];
                    }
                    continue;
                }
                dp[i][j][0]=-1;
                dp[i][j][1]=1;
                if(grid[i][j]>=0){
                    if(i-1>=0){
                        dp[i][j][0]=max(dp[i][j][0],dp[i-1][j][0]*grid[i][j]);
                        dp[i][j][1]=min(dp[i][j][1],dp[i-1][j][1]*grid[i][j]);
                    }
                    if(j-1>=0){
                        dp[i][j][0]=max(dp[i][j][0],dp[i][j-1][0]*grid[i][j]);
                        dp[i][j][1]=min(dp[i][j][1],dp[i][j-1][1]*grid[i][j]);
                    }
                }
                else{
                    if(i-1>=0){
                        dp[i][j][0]=max(dp[i][j][0],dp[i-1][j][1]*grid[i][j]);
                        dp[i][j][1]=min(dp[i][j][1],dp[i-1][j][0]*grid[i][j]);
                    }
                    if(j-1>=0){
                        dp[i][j][0]=max(dp[i][j][0],dp[i][j-1][1]*grid[i][j]);
                        dp[i][j][1]=min(dp[i][j][1],dp[i][j-1][0]*grid[i][j]);
                    }
                }
            }
        }
        long long ans=dp[n-1][m-1][0];
        if(ans<0) return -1;
        return ans%1000000007;
    }
};

D. 连通两组点的最小成本

状压dp. 原问题等价于在 cost 矩阵中选取若干元素,满足矩阵的每一行和每一列都至少有一个元素被选中,同时选中元素的总和最小.

cost 矩阵的列进行状态压缩,dp[i][j] 表示 [0...i] 行与状态 j 对应的列进行连通的最小成本,则状态转移式为.
d p [ i ] [ j ∣ k ] = m a x ( d p [ i ] [ j ∣ k ] , d p [ i − 1 ] [ j ] + g [ i ] [ k ] ) dp[i][j|k]=max(dp[i][j|k],dp[i-1][j]+g[i][k]) dp[i][jk]=max(dp[i][jk],dp[i1][j]+g[i][k])
其中 k 也是压缩状态,上式考虑的是第 i 行元素的选择情况,第 i 行选择状态 k 对应的列,相应的价值之和为 g[i][k],数组 g 可以通过预处理得到,最终答案为 dp[n-1][(1<<m)-1],初始状态为 dp[0].

注意在枚举状态时,从1开始而不是从0开始,因为0表示空集(没有元素被选中),是不合法状态.

有一处可以优化的地方:不必枚举 k 的所有情况,可以分别枚举第 i 行只选择 1 列的情况,以及第 i 行选择若干不在状态 j 中列的情况(因为在状态 j 中的列已经满足要求了,多选择一个元素一定会使得结果更大).

另外还可以通过滚动数组优化空间,将 dp 数组变成一维. 为了便于理解,下面的代码并没有用滚动数组优化.

class Solution {
public:
    const static int inf=1e9;

    int connectTwoGroups(vector<vector<int>>& cost) {
        int n=cost.size(),m=cost[0].size();
        int dp[n][1<<m],g[n][(1<<m)];

        for(int i=0;i<n;++i){
            for(int j=0;j<(1<<m);++j){
                g[i][j]=0;
                for(int k=0;k<m;++k){
                    if(j>>k&1) g[i][j]+=cost[i][k];
                }
            }
        }

        for(int i=0;i<n;++i){
            for(int j=0;j<(1<<m);++j){
                if(!i) dp[i][j]=g[i][j];
                else dp[i][j]=inf;
            }
        }
        for(int i=1;i<n;++i){
            for(int j=1;j<(1<<m);++j){
                for(int k=0;k<m;++k){
                    dp[i][j|(1<<k)]=min(dp[i][j|(1<<k)],dp[i-1][j]+cost[i][k]);
                }
                int tot=(1<<m)-1-j;
                for(int k=tot;k>=1;k=tot&(k-1)){
                    dp[i][j|k]=min(dp[i][j|k],dp[i-1][j]+g[i][k]);
                }
            }
        }
        return dp[n-1][(1<<m)-1];
    }
};

学到了一个新知识点:枚举一个 n 位二进制数 x 对应的所有子集,比如 4 位二进制数 x=1010,那么 x 的所有子集为 1010001010000000. 代码可以这样实现.

for(int y=x;y>=0;y=x&(y-1)){ //对y进行操作 }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值