• [Leetcode Weekly Contest]207


    链接:LeetCode

    [Leetcode]1592. 重新排列单词间的空格

    给你一个字符串 text ,该字符串由若干被空格包围的单词组成。每个单词由一个或者多个小写英文字母组成,并且两个单词之间至少存在一个空格。题目测试用例保证 text 至少包含一个单词 。请你重新排列空格,使每对相邻单词之间的空格数目都 相等 ,并尽可能 最大化 该数目。如果不能重新平均分配所有空格,请 将多余的空格放置在字符串末尾 ,这也意味着返回的字符串应当与原 text 字符串的长度相等。
    返回 重新排列空格后的字符串 。

    以单词分割,统计单词和空格个数即可。

    class Solution {
    public:
        vector<string> split(const string &text,char stop){
            vector<string> res;
            string t;
            for(auto ch:text){
                if(ch==stop){
                    if(t!="") res.push_back(t);
                    t = "";
                }
                else{
                    t += ch;
                }
            }
            if(t!="") res.push_back(t);
            return res;
        }
    
        string reorderSpaces(string text) {
            vector<string> split_res = split(text,' ');
            int n = text.size();
            int m = split_res.size();
            int blank = 0;
            for(auto ch : text) blank += ch==' ' ;
            if(m==1){
                string res = split_res[0];
                for(int i=0;i<blank;++i){
                    res += ' ';
                }
                return res;
            }
    
            int space_join = (int)blank/(m-1);
            int space_back = blank%(m-1);
            string res;
            for(int i=0;i<m-1;i++){
                res += split_res[i];
                for(int j=0;j<space_join;++j){
                    res += ' ';
                }
            }
            res += split_res[m-1];
            for(int i=0;i<space_back;++i){
                res += ' ';
            }
            return res;
        }
    };
    

    [Leetcode]1593. 拆分字符串使唯一子字符串的数目最大

    给你一个字符串 s ,请你拆分该字符串,并返回拆分后唯一子字符串的最大数目。
    字符串 s 拆分后可以得到若干 非空子字符串 ,这些子字符串连接后应当能够还原为原字符串。但是拆分出来的每个子字符串都必须是 唯一的 。
    注意:子字符串 是字符串中的一个连续字符序列。

    根据题意DFS暴力即可。

    class Solution {
    public:
        int res_max = 0;
        unordered_set<string> set;
        int maxUniqueSplit(string s) {
            dfs(s);
            return res_max;
        }
        void dfs(string &s){
            if(s==""){
                res_max = max(res_max,int(set.size()));
            }
            if(s.size()+set.size()<res_max) return;
            for(int i=1;i<=s.size();++i){
                string tmp = s.substr(0,i);
                string rest = s.substr(i);
                if(set.find(tmp)==set.end()){
                    set.insert(tmp);
                    dfs(rest);
                    set.erase(tmp);
                }
            }
        }
    };
    

    [Leetcode]1594. 矩阵的最大非负积

    给你一个大小为 rows x cols 的矩阵 grid 。最初,你位于左上角 (0, 0) ,每一步,你可以在矩阵中 向右 或 向下 移动。在从左上角 (0, 0) 开始到右下角 (rows - 1, cols - 1) 结束的所有路径中,找出具有 最大非负积 的路径。路径的积是沿路径访问的单元格中所有整数的乘积。
    返回 最大非负积 对 109 + 7 取余 的结果。如果最大积为负数,则返回 -1 。
    注意,取余是在得到最大积之后执行的。

    动态规划。设置两个DP数组分别记录路劲最大值dp1和路径最小值dp2,即递推过程:

    [dp1[i][j] = max({dp1[i-1][j]*grid[i][j], dp1[i][j-1]*grid[i][j],dp2[i-1][j]*grid[i][j], dp2[i][j-1]*grid[i][j]}); ]

    [dp2[i][j] = min({dp1[i-1][j]*grid[i][j], dp1[i][j-1]*grid[i][j],dp2[i-1][j]*grid[i][j], dp2[i][j-1]*grid[i][j]}); ]

    注意初始化:for(int i = 1 ; i < row; i++) dp1[i][0] =dp1[i-1][0] * grid[i][0];dp2同理

    class Solution {
    public:
        int maxProductPath(vector<vector<int>>& grid) {
            int row = grid.size();
            int col = grid[0].size();
            vector<vector<long long>>dp1(row,vector<long long>(col));
            vector<vector<long long>>dp2(row,vector<long long>(col));
            dp1[0][0] = grid[0][0];
            dp2[0][0] = grid[0][0];
            for(int i = 1 ; i < row; i++){
                dp1[i][0] =dp1[i-1][0] * grid[i][0];
                dp2[i][0] =dp2[i-1][0] * grid[i][0];
            }
            for(int i = 1 ; i < col; i++){
                dp1[0][i] =dp1[0][i-1] * grid[0][i];
                dp2[0][i] =dp2[0][i-1] * grid[0][i];
    
            }
            for(int i = 1; i < grid.size();i++){
                for(int j = 1; j < grid[0].size();j++){
                    dp1[i][j]=max({dp1[i-1][j]*grid[i][j], dp1[i][j-1]*grid[i][j],dp2[i-1][j]*grid[i][j], dp2[i][j-1]*grid[i][j]});
                    dp2[i][j]=min({dp1[i-1][j]*grid[i][j], dp1[i][j-1]*grid[i][j],dp2[i-1][j]*grid[i][j], dp2[i][j-1]*grid[i][j]});
                }
            }
            if(dp1[row-1][col-1] < 0) return -1;
            else return dp1[row-1][col-1]%1000000007;
        }
    };
    

    [Leetcode]1595. 连通两组点的最小成本

    给你两组点,其中第一组中有 size1 个点,第二组中有 size2 个点,且 size1 >= size2 。
    任意两点间的连接成本 cost 由大小为 size1 x size2 矩阵给出,其中 cost[i][j] 是第一组中的点 i 和第二组中的点 j 的连接成本。如果两个组中的每个点都与另一组中的一个或多个点连接,则称这两组点是连通的。换言之,第一组中的每个点必须至少与第二组中的一个点连接,且第二组中的每个点必须至少与第一组中的一个点连接。
    返回连通两组点所需的最小成本。

    状压DP,或者回溯+贪心+剪枝。问题等价于: 在一个矩阵中选取一些值, 满足矩阵的每一行和每一列都至少有一个元素被选中, 同时选中元素的总和最小 (此矩阵就是 cost 矩阵).
    对于DP,由于矩阵的列数较少, 我们可以用状压 DP 来表示每一行的选取情况, 假设矩阵有 (m)(n) 列, 那么我们维护一个 DP 矩阵 dp[m][1 << n], dp[i][j]表示当前选取到第 (i) 行, 每列的选取状况为 (j) 时总的最小开销, 其中 (j) 的第 (k) 位为 (1) 即表示第 (k) 列已经被选取过了. 那么状态转移方程为

    [dp[i][j|k] = Math.min(dp[i][j|k], dp[i - 1][k] + costMatrix[i][j]) ]

    其中 costMatrix[i][j] 表示第(i)行选取状况为(j)时该行被选取得元素总和.

    另外,可采用贪心加剪枝的方法。贪心,就是在每一行优先选取比较小的代价去进行dfs回溯,那些比较大的代价有可能会被剪枝剪掉。剪枝,如果某条路径当前的代价和已经超过了目前找到的最优代价和,就及时回退。

    class Solution {
    public:
        int connectTwoGroups(vector<vector<int>> &cost) {
            int size1 = cost.size(), size2 = cost[0].size(), stateNum = 1 << size2;    //stateNum为第二组总的状态数+1
            vector<int> dp(stateNum, INT_MAX);                                         //dp数组初始化为很大的数
            dp[0] = 0;                                                                 //初始状态
            for (int i = 0; i < size1; ++i) {                                          //迭代每一行
                vector<int> temp(stateNum, INT_MAX);                                   //滚动数组
                for (int state = 0; state < stateNum; ++state) {                       //枚举所有状态
                    if (dp[state] == INT_MAX) continue;                                //若状态不可达,continue
                    for (int j = 0; j < size2; ++j) {                                  //方案一:任选一条边相连
                        int nextState = state | (1 << j);                              //相连后到达的状态
                        temp[nextState] = min(temp[nextState], dp[state] + cost[i][j]);//更新最小花费
                    }
                    int flipState = (stateNum - 1) ^ state;                                          //方案二:连接若干未连接的边,使用异或进行位反转得到所有未连接的边
                    for (int subState = flipState; subState; subState = flipState & (subState - 1)) {//枚举未连接的边的子集
                        int sum = 0;                                                                 //记录花费
                        for (int k = 0; k < size2; ++k)                                              //枚举size2
                            if (subState & (1 << k)) sum += cost[i][k];                              //若子集中存在该边,则更新花费
                        int nextState = state | subState;                                            //相连后到达的状态
                        temp[nextState] = min(temp[nextState], dp[state] + sum);                     //更新最小花费
                    }
                }
                dp = move(temp);//滚动数组
            }
            return dp.back();//返回结果
        }
    };
    

    下面是回溯+贪心+剪枝的方法。

    class Solution {
        int m,n;
        int ans=2147483647;
        int row_chosen[12]={0};
        int mincost_of_row[12];
        int v[12][12];
        int cost[12][12];
    public:
        inline void fun(int j,int curcost){
            //curcost和rest总是在一起以和的形式出现,所以干脆把他俩合并成curcost
            //逐列进行深搜
            for(int k=0;k<m;k++){
                int i=v[j][k];
                if(!row_chosen[i]){
                    curcost-=mincost_of_row[i];
                    row_chosen[i]++;
                    if(curcost+cost[i][j]<ans){
                        //在这里进行剪枝
                        if(j+1==n)ans=curcost+cost[i][j];
                        else fun(j+1,curcost+cost[i][j]);
                    }
                    row_chosen[i]--;
                    curcost+=mincost_of_row[i];
                }
                else{
                    row_chosen[i]++;
                    if(curcost+cost[i][j]<ans){
                        //在这里进行剪枝
                        if(j+1==n)ans=curcost+cost[i][j];
                        else fun(j+1,curcost+cost[i][j]);
                    }
                    row_chosen[i]--;
                }
            }
        }
        int connectTwoGroups(vector<vector<int>>& _cost) {
            m=_cost.size();
            n=_cost[0].size();
            for(int i=0;i<m;i++)memcpy(cost[i],&_cost[i][0],4*n);
            //将vector<vector<int>>数据送入int[][]
    
            int temp[m];
            for(int i=0;i<m;i++)temp[i]=i;
            for(int j=0;j<n;j++){
                memcpy(v[j],temp,sizeof(temp));
                sort(v[j],v[j]+m,[&](int x,int y)->bool{return cost[x][j]<cost[y][j];});
            }
            //用int[]替换vector<int>
    
            int Sum=0;
            for(int i=0;i<m;i++){
                mincost_of_row[i]=*min_element(cost[i],cost[i]+n);
                Sum+=mincost_of_row[i];
            }
    
            fun(0,Sum);
            //预处理完成后,进行深搜回溯
    
            return ans;
        }
    };
    

    Leetcode
    Leetcode

  • 相关阅读:
    关于图片或者文件在数据库的存储方式归纳
    js默认比较第一个数字大小
    项目中empty遇到的一个问题
    thinkphp 一个页面使用2次分页的方法
    mysql处理海量数据时的一些优化查询速度方法
    项目中的一个和分页总数有关的子查询
    Mysql乱码
    HTML 5 <input> placeholder 属性
    form 表单jquery验证插件使用
    在网站制作中随时可用的10个 HTML5 代码片段
  • 原文地址:https://www.cnblogs.com/hellojamest/p/13720901.html
Copyright © 2020-2023  润新知