十三周 Dynamic programming counting bits

本文介绍了一种使用二分查找法在已排序数组中精确找到特定目标值起始与结束位置的方法。通过两次运用二分查找,分别确定目标值的左边界和右边界,实现了高效搜索。

Problem

Given a sorted array of n integers, find the starting and ending position of a given target value.

If the target is not found in the array, return [-1, -1].

Example

Given [5, 7, 7, 8, 8, 10] and target value 8,
return [3, 4].

首先,建立二元结果数组res,起点start,终点end。
二分法求左边界:
当中点小于target,start移向中点,否则end移向中点;
先判断起点,再判断终点是否等于target,如果是,赋值给res[0]。
二分法求右边界:
当中点大于target,end移向中点,否则start移向中点;
先判断终点,再判断起点是否等于target,如果是,赋值给res[1]

  1. #include <iostream>  
  2. #include <vector>   
  3. using namespace std;  
  4.   
  5. class Solution {  
  6. public:  
  7.     vector<int> searchRange(vector<int>& nums, int target) {  
  8.         if(nums.size() == 0)  
  9.         {  
  10.             return vector<int>(2, -1);  
  11.         }  
  12.   
  13.         vector<int> result;  
  14.         int begin = 0;  
  15.         int end = nums.size()-1;  
  16.   
  17.         int mid;  
  18.         while(begin < end)  
  19.         {  
  20.             mid = (begin + end) / 2;  
  21.             if(target > nums[mid])  
  22.             {  
  23.                 begin = mid + 1;  
  24.             }  
  25.             else  
  26.             {  
  27.                 end = mid;  
  28.             }  
  29.         }  
  30.   
  31.         if(nums[begin] != target)  
  32.         {  
  33.             return vector<int>(2, -1);  
  34.         }  
  35.   
  36.         result.push_back(begin);  
  37.         end = nums.size()-1;  
  38.           
  39.         while(begin < end)  
  40.         {  
  41.             if(begin == end - 1)  
  42.             {  
  43.                 if(target == nums[end])  
  44.                 {  
  45.                     break;  
  46.                 }  
  47.                 else  
  48.                 {  
  49.                     end = end - 1;  
  50.                     break;  
  51.                 }  
  52.             }  
  53.   
  54.             mid = (begin + end) / 2;  
  55.             if(target == nums[mid])  
  56.             {  
  57.                 begin = mid;  
  58.             }  
  59.             else  
  60.             {  
  61.                 end = mid - 1;  
  62.             }  
  63.         }  
  64.   
  65.         result.push_back(end);  
  66.   
  67.         return result;  
  68.     }  
  69. };  
  70.   
  71. int main()  
  72. {  
  73.     int a[6] = {8, 8, 8, 8, 8, 9};  
  74.     vector<int> test(a, a+6);  
  75.   
  76.     Solution ss;  
  77.   
  78.     vector<int> result = ss.searchRange(test, 9);  
  79.   
  80.     cout << result[0] << "," <<  result[1] << endl;  
  81.   
  82.     return 0;  
  83. }  

翻译成中文并用latex渲染: First, let's see how many zebra-Like numbers less than or equal to 1018 exist. It turns out there are only 30 of them, and based on some zebra-like number zi , the next one can be calculated using the formula zi+1=4⋅zi+1 . Then, we have to be able to quickly calculate the zebra value for an arbitrary number x . Since each subsequent zebra-like number is approximately 4 times larger than the previous one, intuitively, it seems like a greedy algorithm should be optimal: for any number x , we can determine its zebra value by subtracting the largest zebra-like number that does not exceed x , until x becomes 0 . Let's prove the correctness of the greedy algorithm: Assume that y is the smallest number for which the greedy algorithm does not work, meaning that in the optimal decomposition of y into zebra-like numbers, the largest zebra-like number zi that does not exceed y does not appear at all. If the greedy algorithm works for all numbers less than y , then in the decomposition of the number y , there must be at least one number zi−1 . And since y−zi−1 can be decomposed greedily and will contain at least 3 numbers zi−1 , we will end up with at least 4 numbers zi−1 in the decomposition. Moreover, there will be at least 5 numbers in the decomposition because 4⋅zi−1<zi , which means it is also less than y . Therefore, if the fifth number is 1 , we simply combine 4⋅zi−1 with 1 to obtain zi ; otherwise, we decompose the fifth number into 4 smaller numbers plus 1 , and we also combine this 1 with 4⋅zi−1 to get zi . Thus, the new decomposition of the number y into zebra-like numbers will have no more numbers than the old one, but it will include the number zi — the maximum zebra-like number that does not exceed y . This means that y can be decomposed greedily. We have reached a contradiction; therefore, the greedy algorithm works for any positive number. Now, let's express the greedy decomposition of the number x in a more convenient form. We will represent the decomposition as a string s of length 30 consisting of digits, where the i -th character will denote how many zebra numbers zi are present in this decomposition. Let's take a closer look at what such a string might look like: si∈{0,1,2,3,4} ; if si=4 , then for any j<i , the character sj=0 (this follows from the proof of the greedy algorithm). Moreover, any number generates a unique string of this form. This is very similar to representing a number in a new numeric system, which we will call zebroid. In summary, the problem has been reduced to counting the number of numbers in the interval [l,r] such that the sum of the digits in the zebroid numeral system equals x . This is a standard problem that can be solved using dynamic programming on digits. Instead of counting the suitable numbers in the interval [l,r] , we will count the suitable numbers in the intervals [1,l] and [1,r] and subtract the first from the second to get the answer. Let dp[ind][sum][num_less_m][was_4] be the number of numbers in the interval [1,m] such that: they have ind+1 digits; the sum of the digits equals sum ; num_less_m=0 if the prefix of ind+1 digits of the number m is lexicographically greater than these numbers, otherwise num_less_m=1 ; was_4=0 if there has not been a 4 in the ind+1 digits of these numbers yet, otherwise was_4=1 . Transitions in this dynamic programming are not very difficult — they are basically appending a new digit at the end. The complexity of the solution is O(log2A) , if we estimate the number of zebra-like numbers up to A=1018 as logA .
最新发布
08-26
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值