zoukankan      html  css  js  c++  java
  • [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

  • 相关阅读:
    LeetCode OJ String to Integer (atoi) 字符串转数字
    HDU 1005 Number Sequence(AC代码)
    HDU 1004 Let the Balloon Rise(AC代码)
    HDU 1003 Max Sum(AC代码)
    012 Integer to Roman 整数转换成罗马数字
    011 Container With Most Water 盛最多水的容器
    010 Regular Expression Matching 正则表达式匹配
    007 Reverse Integer 旋转整数
    006 ZigZag Conversion
    005 Longest Palindromic Substring 最长回文子串
  • 原文地址:https://www.cnblogs.com/hellojamest/p/13720901.html
Copyright © 2011-2022 走看看