数学和模拟
来源:互联网 发布: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; }};
- 数学和模拟
- XMU-1349-数学+模拟
- GDOI2016模拟8.19数学
- 【9108】模拟数学计算器
- 模拟数学:除法
- 堆栈模拟数学表达式
- [NOIP模拟][数学]Fibonacci
- codeforces864C(模拟、数学)
- hdu_1049 Climbing Worm(数学,模拟)
- Gym 100499I (数学 模拟)
- [NOIP模拟][数学推理]Math
- 【二分】【前缀和】【数学归纳法】【优化】NOIP模拟测试题"nan"
- (前缀和排序)NOIP2017提高组模拟 Day1P1 小A的数学
- 【模拟高精度+进制转换】数学序列
- 数学建模时候的蒙特卡洛模拟
- 2012数学建模第一轮模拟总结
- poj-3899-The Lucky Numbers 模拟+数学
- Balls Rearrangement(hdu4611,模拟+数学)
- C#控制台 输出hello world之 用结构的静态构造函数输出
- HDOJ_1425 sort
- 逻辑编程在clojure中的应用:clojure.core.logic
- 启动activit
- 关于display的属性的那些事
- 数学和模拟
- C#控制台 输出hello world之 使用分部类(不用分部方法)输出
- java8中对ConcurrentHashMap的改进
- OpenGLES入门笔记 :OpenGLES光照基础总结4 光照的顶点计算与片元计算
- mysql三例死锁场景分析
- GridControl全选和反选
- 2.《深入理解Java虚拟机》Hotspot虚拟机中的对象创建与访问定位
- VS下的解决方案目录结构设置和管理
- 网易——求二叉树最大叶子节点到最小叶子节点的距离