[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

posted @ 2020-09-23 20:36  Jamest  阅读(158)  评论(0编辑  收藏  举报