2019LeetCode第160场周赛

5238. 找出给定方程的正整数解

题目描述

给出一个函数 f(x, y) 和一个目标结果 z,请你计算方程 f(x,y) == z 所有可能的正整数 数对 x 和 y。
给定函数是严格单调的,也就是说:
f(x, y) < f(x + 1, y)
f(x, y) < f(x, y + 1)

函数接口定义如下:
interface CustomFunction {
public:
// Returns positive integer f(x, y) for any given positive integer x and y.
int f(int x, int y);
};

如果你想自定义测试,你可以输入整数 function_id 和一个目标结果 z 作为输入,其中 function_id 表示一个隐藏函数列表中的一个函数编号,题目只会告诉你列表中的 2 个函数。
你可以将满足条件的 结果数对 按任意顺序返回。

示例 1:
输入:function_id = 1, z = 5
输出:[[1,4],[2,3],[3,2],[4,1]]
解释:function_id = 1 表示 f(x, y) = x + y

示例 2:
输入:function_id = 2, z = 5
输出:[[1,5],[5,1]]
解释:function_id = 2 表示 f(x, y) = x * y

提示:
1 <= function_id <= 9
1 <= z <= 100
题目保证 f(x, y) == z 的解处于 1 <= x, y <= 1000 的范围内。
在 1 <= x, y <= 1000 的前提下,题目保证 f(x, y) 是一个 32 位有符号整数。

题解1

首先要理解题意,然后x,y是正整数,最大只能是1000。可能的组合也只有 1000 ∗ 1000 1000 * 1000 10001000种。完全可以用暴力法求解出来。

代码1

/*
 * // This is the custom function interface.
 * // You should not implement it, or speculate about its implementation
 * class CustomFunction {
 * public:
 *     // Returns f(x, y) for any given positive integers x and y.
 *     // Note that f(x, y) is increasing with respect to both x and y.
 *     // i.e. f(x, y) < f(x + 1, y), f(x, y) < f(x, y + 1)
 *     int f(int x, int y);
 * };
 */
/*
暴力求解
*/
class Solution {
public:
    vector<vector<int>>res;
    vector<vector<int>> findSolution(CustomFunction& customfunction, int z) {
        for(int i = 1; i <= 1000; ++i){
            for(int j = 1; j <= 1000; ++j){
                if(customfunction.f(i, j) == z){
                    res.push_back({i, j});
                }
            }
        }
        return res;
    }
};

题解2

因为函数是严格单调的,首先代码1,可以加一个break判断,当 c u s t o m f u n c t i o n . f ( i , j ) ≥ z customfunction.f(i, j) \geq z customfunction.f(i,j)z时,可以跳出内层循环。

5239. 循环码排列

题目描述

给你两个整数 n 和 start。你的任务是返回任意 (0,1,2,…,2^n-1) 的排列 p,并且满足:
p[0] = start
p[i] 和 p[i+1] 的二进制表示形式只有一位不同
p[0] 和 p[2^n -1] 的二进制表示形式也只有一位不同

示例 1:
输入:n = 2, start = 3
输出:[3,2,0,1]
解释:这个排列的二进制表示是 (11,10,00,01)
所有的相邻元素都有一位是不同的,另一个有效的排列是 [3,1,0,2]

示例 2:
输出:n = 3, start = 2
输出:[2,6,7,5,4,0,1,3]
解释:这个排列的二进制表示是 (010,110,111,101,100,000,001,011)

提示:
1 <= n <= 16
0 <= start < 2^n

题解1

第一反应就是格雷码和自然码。但是忘记转换规则。
变换规则与相关知识,可以参见博客维基百科
题目中给出的start,相当于是格雷码,先求出对应的自然码,然后逐渐递增。再由自然码,求出格雷码。有了思路很容易写出代码, 这里比赛的时候代码写的很纯朴(fù zá)

代码1


class Solution {
public:
    vector<int>res, tmp;
    vector<int> circularPermutation(int n, int start) {
        //求出起始的自然码
        const int MOD = pow(2, n);
        //cout << MOD << endl;
        res.push_back(start);
        int num = start;
        for(int i = 0; i < n; ++i){
            tmp.push_back(num % 2);
            num = num / 2;
        }
        reverse(tmp.begin(), tmp.end());
        num = tmp[0];
        for(int i = 1; i < n; ++i){//求start的自然码
            tmp[i] = tmp[i] ^ tmp[i - 1];
            num = num * 2 + tmp[i];
        }
        for(int cnt = 0; cnt < MOD - 1; ++cnt){//求格雷码
            num = (num + 1) % MOD;
            tmp.clear();
            int i = num;
            for(int j = 0; j < n; ++j){
                tmp.push_back(i % 2);
                i = i / 2;
            }
            
            for(int j = 0; j < n - 1; ++j){
                tmp[j] = tmp[j] ^ tmp[j + 1];
            }
            reverse(tmp.begin(), tmp.end());
            int num1 = 0;
            for(int j = 0; j < n; ++j){//求出格雷码对应十进数
                num1 = num1 * 2 + tmp[j];
            }
            res.push_back(num1);
        }
        return res;
    }
};

题解2

对代码1进行优化,刚才写法太耿直了,我们可以整点高大上的写法。
自然码转格雷码如公式1。
自 然 码 x 转 格 雷 码 y : y = x ∧ ( x > > 1 ) (1) 自然码x转格雷码y:y = x \wedge (x>>1)\tag{1} xyy=x(x>>1)(1)
格雷码转自然码如以下代码。

/*
x是格雷码
y是自然码(每一位与前面的所有位都异或)
*/
int y = x;
while(x>>=1)
    y ^= x;

代码2


class Solution {
public:
    vector<int>res, tmp;
    vector<int> circularPermutation(int n, int start) {
        //求出起始的自然码
        const int MOD = pow(2, n);
        res.push_back(start);
        int num = start;
        while(start >>= 1)//求start对应的格雷码
            num ^= start;
        for(int cnt = 0; cnt < MOD - 1; ++cnt){//自然码+1,求对应的格雷码
            num = (num + 1) % MOD;
            int num1 = num ^ (num >> 1);
            res.push_back(num1);
        }
        return res;
    }
};

题解3

这题也可以用暴力搜索来求解,下一个数字产生只需要将前一个数对应的二进制位修改一个,需要用一个mark来标记已产生的数,保证重复,还需要难证最后一个数和第一个数只相差一个二进制位。

代码3

略,可参照bilibili刀哥讲解

5240. 串联字符串的最大长度

题目描述

给定一个字符串数组 arr,字符串 s 是将 arr 某一子序列字符串连接所得的字符串,如果 s 中的每一个字符都只出现过一次,那么它就是一个可行解。
请返回所有可行解 s 中最长长度。

示例 1:
输入:arr = [“un”,“iq”,“ue”]
输出:4
解释:所有可能的串联组合是 “”,“un”,“iq”,“ue”,“uniq” 和 “ique”,最大长度为 4。

示例 2:
输入:arr = [“cha”,“r”,“act”,“ers”]
输出:6
解释:可能的解答有 “chaers” 和 “acters”。

示例 3:
输入:arr = [“abcdefghijklmnopqrstuvwxyz”]
输出:26

提示:
1 <= arr.length <= 16
1 <= arr[i].length <= 26
arr[i] 中只含有小写英文字母

题解1

最多只有16个串,其所有的子集也只是 2 16 2^{16} 216。可以用暴力求解,有点类似集合幂集的求法(其实就是),具体见代码。

代码1

class Solution {
public:
    bool mark[26];
    int maxLength(vector<string>& arr) {
        int n = arr.size();
        int maxLen = 0;
        const int NUM = pow(2, n);
        for(int i = 1; i < NUM; ++i){//2 * n - 1种情况
            int tmp = i;
            memset(mark, false, sizeof(mark));//标记字符是否出现
            int cnt = 0;
            bool flag = true;
            for(int j = 0; j < n; ++j){
                if(tmp % 2){//判断对应的二进制位
                    for(int k = 0; k < arr[j].size(); ++k){
                        if(!mark[arr[j][k] - 'a']){
                            mark[arr[j][k] - 'a'] = true;
                            ++cnt;
                        }
                        else{
                            flag = false;
                            break;
                        }
                    }
                }
                tmp = tmp / 2;
            }
            if(flag && cnt > maxLen){
                maxLen = cnt;
            }
        }
        return maxLen;
    }
};

5241. 铺瓷砖

题目描述

你是一位施工队的工长,根据设计师的要求准备为一套设计风格独特的房子进行室内装修。
房子的客厅大小为 n x m,为保持极简的风格,需要使用尽可能少的 正方形 瓷砖来铺盖地面。
假设正方形瓷砖的规格不限,边长都是整数。
请你帮设计师计算一下,最少需要用到多少块方形瓷砖?

示例 1:
题目描述1
输入:n = 2, m = 3
输出:3
解释:3 块地砖就可以铺满卧室。
2 块 1x1 地砖
1 块 2x2 地砖

示例 2:
题目描述2
输入:n = 5, m = 8
输出:5

示例 3:
题目描述3
输入:n = 11, m = 13
输出:6

题解1

没思路,第一名竟然打表,好多人说要举报他。能体会这种心情。
这个题是np问题,所有的多项式解法都有问题,或者说只能是面向数据的解法,数据量增大时,不再适用。
当然这题也可以用暴力求解。参照panpeng代码

代码1

class Solution {
public:
    int ans;
    int N,M;
    vector<vector<int>> Map;
    void dfs(int x,int y,int num){

        if(x>=N){
            ans=min(ans,num);
            return;
        }
        if(num>=ans){
            return;
        }
        int nx=x;   ///下一个迭代的位置
        int ny=y;  
        if(ny<M-1){
            ny++;
        }else{
            nx++;
            ny=0;
        }
        if(Map[x][y]==1){
            dfs(nx,ny,num);
            return;
        }
        铺砖
        int i=y;
        for(;i<M;i++){
            if(Map[x][i]==1){
                break;   //不能铺了
            }
            if(x+i-y>=N){
                break;    //不能铺了
            }
        }
        int len=i-y;
        for(int len=i-y;len>0;len--){
            for(int i=0;i<len;i++){
                for(int j=0;j<len;j++){
                    Map[x+i][y+j]=1;
                }
            } 
            
            dfs(nx,ny,num+1);
            for(int i=0;i<len;i++){
                for(int j=0;j<len;j++){
                    Map[x+i][y+j]=0;
                }
            }            
        }

    }
    int tilingRectangle(int n, int m) {
        Map=vector<vector<int>>(n,vector<int>(m));
        ans=n*m;
        N=n;
        M=m;
        dfs(0,0,0);
        return ans;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值