数学和模拟

来源:互联网 发布:matlab生成复杂网络 编辑:程序博客网 时间:2024/06/11 00:31

思想:利用数学规律或者模拟
内容:注意next_permutation的求取方式
Trapping Rain Water和Candy的单调方式
Single Num涉及到的位运算
11、Next Permutation
纯数学题,/(ㄒoㄒ)/~~

class Solution {public:    void nextPermutation(vector<int>& nums) {        int n=nums.size();        if(n<=1) return;        //从右往左找到第一个破坏升序的元素        int i;        for(i=n-2;i>=0 && nums[i]>=nums[i+1];i--);        //记录这个位置        int pivot=i;        if(pivot>=0){            //从右往左找一个比这个数大的            for(i=n-1;i>=0 && nums[i]<=nums[pivot];i--);            i=i>=0?i:0;            //交换这两个数            int temp=nums[pivot];            nums[pivot]=nums[i];            nums[i]=temp;        }        //将从pivot开始以后的数据全部反向        for(int j=pivot+1,k=n-1;j<k && j<n-1 && k>=0;j++,k--){            int temp=nums[j];            nums[j]=nums[k];            nums[k]=temp;        }    }};

12、求一个数n所构成的排列的第k个
康托展开….
这个有点没耐心写下去了,就写个思路吧
托展开的公式:(不用记,看形势就行,下面会有例子)

X=an*(n-1)!+an-1*(n-2)!+…+ai*(i-1)!+…+a2*1!+a1*0!
ai为整数,并且0<=ai < i(1<=i<=n)
适用范围:没有重复元素的全排列

第一类题:N个数的第k个排序,例子,1,2,3,4共有4!种排列,1234,1243,1324等等。按顺序应该是
1234
1243
1324
1342
1423
1432等等
可以通过STL中next_permutation(begin, end);来算下一个全排列,理论上你要算n个数的第k个排列只要调用k-1次next_permutation()就行,但是一般来说肯定会超时的,因为next_permutation的时间复杂度是O(n)(如果自己写出来next_permutation时间复杂度比n大就要注意了,其中一个容易疏忽的地方是最后排序可以用reverse而不是sort)。所以如果用这个的话时间复杂度是O(N^2)。

而用康托展开只要O(n)就行,下面来说说具体怎么做:
题目:找出第16个n = 5的序列(12345)
首先第十六个也就是要前面有15个数,要调用15次next_permutation函数。
根据第一行的那个全排列公式,15 / 4! = 0 …15 =》 有0个数比他小的数是1,所以第一位是1
拿走刚才的余数15,用15 / 3! = 2 …3 => 剩下的数里有两个数比他小的是4(1已经没了),所以第二位是4
拿走余数3, 用 3 / 2! = 1 …1 =》 剩下的数里有一个数比他小的是3,所以第三位是3
拿走余数1, 用 1/ 1! = 1 …0 => 剩下的数里有一个数比他小的是 5(只剩2和5了),所以第四位是5
所以排列是 1,4,3,5,2

第二类题:已知是n = 5,求14352是它的第几个序列?(同一道题)
用刚才的那道题的反向思维:

第一位是1,有0个数小于1,即0* 4!
第二位是4,有2个数小于4,即2* 3!
第三位是3,有1个数小于3,即1* 2!
第四位是5,有1个数小于5,即1* 1!
第五位是2,不过不用算,因为肯定是0
所以14352是 n = 5的第 0 + 12 + 2 + 1 + 0 = 15 + 1(求的是第几个,所以要加一) = 16
第16个,跟刚才那道题一样,证明对了
来源:http://blog.csdn.net/modiziri/article/details/22389303?utm_source=tuicool&utm_medium=referral

13、Valid Sudoku(判断数独)
没什么难度,纯模拟

class Solution {public:    bool isValidSudoku(vector<vector<char>>& board) {        int visit[10];        for(int i=0;i<9;i++){            //9行            memset(visit,0,sizeof(visit));            for(int j=0;j<9;j++){                if(board[i][j]=='.') continue;                if(visit[board[i][j]-'0']>0)                return false;                else visit[board[i][j]-'0']=1;            }            //9列            memset(visit,0,sizeof(visit));            for(int j=0;j<9;j++){                if(board[j][i]=='.') continue;                if(visit[board[j][i]-'0']>0)                return false;                else visit[board[j][i]-'0']=1;            }        }        //9个小方格        for(int i=0;i<3;i++){            for(int j=0;j<3;j++){                //第i行j列的小正方形                memset(visit,0,sizeof(visit));                for(int a=0;a<3;a++){                    for(int b=0;b<3;b++){                        if(board[i*3+a][j*3+b]=='.') continue;                        if(visit[board[i*3+a][j*3+b]-'0']>0)                        return false;                        else visit[board[i*3+a][j*3+b]-'0']=1;                    }                }            }        }        return true;    }};

14、Trapping Rain Water
感觉这是一道思维题…
Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.

For example,
Given [0,1,0,2,1,0,1,3,2,1,2,1], return 6.

图片描述
于是我写的代码不能够处理W形的地形
因为我所判断只是局部最高
正确的方法应当是找到最高点,然后找左单调和右单调

class Solution {public:    int trap(vector<int>& height) {        vector<int> pivot;        vector<int> index;        int n=height.size();        if(n<2) return 0;        for(int i=0;i<n;i++){            //判断点的类型            if(isHeigh(height,i)){                pivot.push_back(height[i]);                index.push_back(i);            }        }        if(pivot.size()<=0){            return 0;        }        int sum=0;        //通过上面,我们知道在k处有制高点pivot        for(int i=0;i<pivot.size()-1;i++){            //得到两个相邻制高点的较小的点            int val=min(pivot[i],pivot[i+1]);            // printf("%d %d %d\n",val,index[i],index[i+1]);            //计算中间的水分            for(int j=index[i]+1;j<index[i+1];j++){                // printf("%d ",height[j]);                if(height[j]>val) continue;                sum+=val-height[j];            }        }        return sum;    }    static int min(int a,int b){        return a>b?b:a;    }    //判断pos位置是否是制高点    static bool isHeigh(vector<int>& height,int pos){        int n=height.size();        if(pos==0 && height[0]>height[1]) return true;        else if(pos==n-1 && height[n-1]>height[n-2]) return true;        else if(height[pos]>height[pos-1] && height[pos]>height[pos+1]) return true;        else return false;    }};

正确的做法应当是找到最高点,再从左到最高点扫描,如果当前的地形低于已经出现的最高高度,则可以储水。

class Solution {public:    int trap(vector<int>& height) {        int n=height.size();        if(n<=2) return 0;        int max=-1,pos=0;        //求取最高点        for(int i=0;i<n;i++){            if(height[i]>max){                max=height[i];                pos=i;            }        }        //从左往右        int sum=0,pivot=0;        for(int i=0;i<=pos;i++){            if(height[i]>pivot){                pivot=height[i];            }            else sum+=pivot-height[i];        }        //从右往左        pivot=0;        for(int i=n-1;i>=pos;i--){            if(height[i]>pivot){                pivot=height[i];            }            else sum+=pivot-height[i];        }        return sum;    }};

15、48.Rotate Image
要求对一个nxn的矩阵进行旋转,不可以申请额外矩阵空间

//一直在想怎么控制成左上角的三角形//后来得知j=n-i就可以妥妥保证二者大小class Solution {public:    void rotate(vector<vector<int>>& matrix) {        int n=matrix.size();        for(int i=0;i<n;i++){            for(int j=0;j<n-i;j++){                swap(matrix[i][j],matrix[n-1-j][n-1-i]);            }        }        for(int i=0;i<n/2;i++){            for(int j=0;j<n;j++){                swap(matrix[i][j],matrix[n-1-i][j]);            }        }    }    static void swap(int &a,int &b){        int temp=a;        a=b;        b=temp;    }};

16、66.Plus One
高精度加法

class Solution {public:    vector<int> plusOne(vector<int>& digits) {        int n=digits.size();        digits[n-1]++;        int c=0,v=0;        for(int i=n-1;i>=0;i--){            v=digits[i]+c;            digits[i]=v%10;            c=v/10;        }        if(c>0)        digits.insert(digits.begin(),1);        return digits;    }};

17、70.Climbing Stairs
爬楼梯,典型的斐波那契数列题
1、递归,直接爆了
2、递推,过了,不过排名很靠后
3、数学公式(卧槽ヾ(。`Д´。))

class Solution {public:    int climbStairs(int n) {        // if(n<=1) return 1;        // else return climbStairs(n-1)+climbStairs(n-2);        if(n<=1) return 1;        if(n==2) return 2;        int a=1,b=2,count=2;        while(count++<n){            int temp=b;            b=a+b;            a=temp;        }        return b;    }};

//使用斐波那契数列通项公式,可以一步得到

class Solution {public:    int climbStairs(int n) {        double s=sqrt(5);        return floor((pow((1+s)/2,n+1)-pow((1-s)/2,n+1))/s+0.5);    }};

18、89.格雷码
对于格雷码的理解
http://blog.csdn.net/beiyeqingteng/article/details/7044471
只要知道了格雷码是如何互相转换的,就可以很轻松地写出
二进制码转格雷码
格雷码转二进制码
格雷码转二进制码

class Solution {public:    vector<int> grayCode(int n) {        vector<int> result;        int v=pow(2,n);        for(int i=0;i<v;i++){            result.push_back(i^(i>>1));        }        return result;    }};

19、73.Set Matrix Zeros
将数组元素某一行列包含0扩展到该整行整列
于是可以利用2个数组作记录

class Solution {public:    void setZeroes(vector<vector<int>>& matrix) {        int m=matrix.size();        int n=matrix[0].size();        vector<int> row;        vector<int> column;        for(int i=0;i<m;i++){            for(int j=0;j<n;j++){                if(matrix[i][j]==0){                    row.push_back(i);                    column.push_back(j);                }            }        }        for(int i=0;i<row.size();i++){            for(int j=0;j<n;j++)            matrix[row[i]][j]=0;        }        for(int i=0;i<column.size();i++){            for(int j=0;j<m;j++)            matrix[j][column[i]]=0;        }    }};

优化存储的方法,可以利用第一行的数据进行操作
代码相对来说复杂不少

class Solution {public:    void setZeroes(vector<vector<int>>& matrix) {        int m=matrix.size();        int n=matrix[0].size();        bool row=false,column=false;        //对第一行特殊处理        for(int i=0;i<n;i++){            if(matrix[0][i]==0){                row=true;                break;            }        }        //第一列特殊处理        for(int i=0;i<m;i++){            if(matrix[i][0]==0){                column=true;                break;            }        }        //使用第一行和第一列进行存储        //故先不统计这一行        for(int i=1;i<m;i++){            for(int j=1;j<n;j++){                if(matrix[i][j]==0){                    matrix[i][0]=0;                    matrix[0][j]=0;                }            }        }        //置零        for(int i=1;i<m;i++){            for(int j=1;j<n;j++){                if(matrix[i][0]==0 || matrix[0][j]==0){                    matrix[i][j]=0;                }            }        }        //此时第一行的数据已经被破坏,需要经过row和column来判断是否之前有0        if(row){            for(int i=0;i<n;i++)            matrix[0][i]=0;        }        if(column){            for(int i=0;i<m;i++){                matrix[i][0]=0;            }        }    }};

20、Gas Station
网上有说这道题是 和最大连续子序列 的变体,可以用动态规划
http://www.cnblogs.com/felixfang/p/3814463.html
关于讲和最大连续子序列,还有这篇文章
http://blog.csdn.net/superchanon/article/details/8228212

a. 最开始,站点0是始发站,假设车开出站点p后,油箱空了,假设sum1 = diff[0] +diff[1] + … + diff[p],可知sum1 < 0;

b. 根据上面的论述,我们将p+1作为始发站,开出q站后,油箱又空了,设sum2 = diff[p+1] +diff[p+2] + … + diff[q],可知sum2 < 0。

c. 将q+1作为始发站,假设一直开到了未循环的最末站,油箱没见底儿,设sum3 = diff[q+1] +diff[q+2] + … + diff[size-1],可知sum3 >= 0。

要想知道车能否开回 q 站,其实就是在sum3 的基础上,依次加上 diff[0] 到 diff[q],看看sum3在这个过程中是否会小于0。但是我们之前已经知道 diff[0] 到 diff[p-1] 这段路,油箱能一直保持非负,因此我们只要算算sum3 + sum1是否 <0,就知道能不能开到 p+1站了。如果能从p+1站开出,只要算算sum3 + sum1 + sum2 是否 < 0,就知都能不能开回q站了。

因为 sum1, sum2 都 < 0,因此如果 sum3 + sum1 + sum2 >=0 那么 sum3 + sum1 必然 >= 0,也就是说,只要sum3 + sum1 + sum2 >=0,车必然能开回q站。而sum3 + sum1 + sum2 其实就是 diff数组的总和 Total,遍历完所有元素已经算出来了。因此 Total 能否 >= 0,就是是否存在这样的站点的 充分必要条件。

于是自己硬着头皮写了一个模拟,TLE

class Solution {public:    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {        int n=cost.size();        //将任意一个设置为出发点        for(int i=0;i<n;i++){            int tank=gas[i];            int startpos=i;//开始地点            int pos=i,count=0;            while(count<n){                //油不足够继续走了                if(tank<cost[pos]){                    break;                }                else{                    tank-=cost[pos];                    pos=(pos+1)%n;//位置是循环的                    tank+=gas[pos];//油量再填充                    count++;//走了多少站                }            }            if(count==n) return startpos;        }        return -1;    }};

正确的方法是走到某一站如果油量<0,那么应当从这一站出发。

class Solution {public:    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {        int n=cost.size();        int total=0,start=0;        int sum=0;        //将任意一个设置为出发点        for(int i=0;i<n;i++){            total+=gas[i]-cost[i];            //走到第i站发现空了            if(sum<0){                sum=gas[i]-cost[i];                start=i;            }            else{                sum+=gas[i]-cost[i];            }        }        return total>=0?start:-1;    }};

21、Candy

这道题和存雨水那道题非常之相似,也是寻找单调序列。
相比之下简单一点的是只需要考虑局部最高点即可
即局部ratings[i]最高的孩子,必然会得到更多的糖果,而其他最高的不会受到影响
http://www.cnblogs.com/felixfang/p/3620086.html

class Solution {public:    int candy(vector<int>& ratings) {        int n=ratings.size();        vector<int> result;        //每个孩子先发一个糖果        for(int i=0;i<n;i++)        result.push_back(1);        //从左往右找递增的        for(int i=0;i<n-1;i++){            if(ratings[i]<ratings[i+1]){                result[i+1]=result[i]+1;            }        }        //从右往左找递增        for(int i=n-1;i>0;i--){            if(ratings[i]<ratings[i-1]){                //如果已经符合大小要求,则不需要处理                if(result[i-1]<=result[i])                result[i-1]=result[i]+1;            }        }        int sum=0;        for(int i=0;i<n;i++)        sum+=result[i];        return sum;    }};

22、Single Num
一个数组里,所有元素都是2个,除了一个元素只有1个。
请找出这个元素

class Solution {public:    int singleNumber(vector<int>& nums) {        int n=nums.size();        int result=0;        for(int i=0;i<n;i++)        result=result^nums[i];        return result;    }};

23、Single Num II
每个数据都是3个,除了一个元素只有一个
请找出这个元素

class Solution {public:    int singleNumber(vector<int>& nums) {        int count[35];        for(int i=0;i<32;i++) count[i]=0;        int n=nums.size();        int result=0;        for(int i=0;i<32;i++){            for(int j=0;j<n;j++){                if((nums[j]>>i) & 1){                    count[i]++;                }            }            result=result | ((count[i]%3)<<i);        }        return result;    }};

24、260.Single Num III
相比较single num,这里要求找出来两个单个数字的
使用异或我们可以得到那两个数字异或的值
从最后一位开始找,如果这一位是0,表示二者相同,所以需要找第一位是1的,说明这两个数在这一位一个是0 一个是1,然后break
根据这个线索,可以将原有数据分为两类,该位是1的和是0的
分别求异或就可以得到想要的两个数据

class Solution {public:    vector<int> singleNumber(vector<int>& nums) {        int n=nums.size();        int temp=0;        for(int i=0;i<n;i++){            temp^=nums[i];        }        int divider=1;        while(true){            if(temp & divider){                break;            }            divider<<=1;        }        //利用divider可以将所有数据分离成两部分        int a=0,b=0;        for(int i=0;i<n;i++){            if(nums[i] & divider){                a^=nums[i];            }            else{                b^=nums[i];            }        }        vector<int> result;        result.push_back(a);        result.push_back(b);        return result;    }};
0 0
原创粉丝点击