经典数学问题<手电过河问题>的动态解法--问题规模扩展至任意大小
非常有趣的一件事是今天在TopCoder的1000分题里面发现了这道经典数学问题。
Notes |
|||||||
- | In an optimal solution, exactly two people will be sent across the bridge with the flashlight each time (if possible), and exactly one person will be sent back with the flashlight each time. In other words, in an optimal solution, you will never send more than one person back from the far side at a time, and you will never send less than two people across to the far side each time (when possible). | ||||||
Constraints |
|||||||
- | times will have between 1 and 6 elements, inclusive. | ||||||
- | Each element of times will be between 1 and 100, inclusive. | ||||||
Examples |
|||||||
0) | |||||||
|
|||||||
1) | |||||||
|
|||||||
2) | |||||||
|
|||||||
3) | |||||||
|
计算的这道题方法其实类似于动态规划,关键在于寻找最优子结构
1)问题的最优子结构是这样推出的
1.每一个人都得过河
2.由1可以知道cost最大的一个也必须过河
3.由2可知必然有一次过河的代价为cost(max)
4.由3可知,在将cost最大的人送过河的运输中最优的方案是将cost第二大的人也同时过河
因此问题可以转化为如何将cost第一大和第二大的两个人同时送过河
2)最优化问题的解法在于首先将cost最小的两个人先送过河然后选择其一送回手电筒(无论哪个人都一样),然后再使cost最大和第二大的两个人同时过河,再另上一次剩在另一 岸的cost最小或者次小的人送回手电筒
因此每次将一对人送过河的cost=iMax1st+(iMin2nd+2*iMin1st)
3)按总人数的奇数偶数可以将整个问题循环之后分支为两个子问题(显而易见,不多赘述)
4)利用大根堆和小根堆使遍历的时间复杂度从n降低至logn
多次实验后代码如下(以下大部分是大根堆小根堆的搭建代码):
1 #include<set> 2 #include<vector> 3 #include<set> 4 #include<vector> 5 #include<iostream> 6 7 using namespace std; 8 9 #define HEAP_SIZE 1024 10 ////////////////////////////////////////// 11 template<typename T> 12 class MaxHeap{ 13 T*arrData; 14 int top; 15 ////////////Filters Up&Down 16 17 void swap(T*l,T*r){ 18 T temp=*l; 19 *l=*r; 20 *r=temp; 21 } 22 23 ////////////////////////////////////////// 24 void FilterUp(){ 25 int kid=top-1; 26 int parent=(kid-1)/2; 27 while(arrData[kid]>arrData[parent]){ 28 swap(&arrData[kid],&arrData[parent]); 29 kid=parent; 30 parent=(parent-1)/2; 31 } 32 } 33 ///////////////////////////////////////// 34 void FilterDown(){ 35 int parent=0; 36 int kid=arrData[parent*2+1]>arrData[parent*2+2]?parent*2+1:parent*2+2; 37 while(kid<top&&arrData[kid]>arrData[parent]){ 38 swap(&arrData[kid],&arrData[parent]); 39 parent=kid; 40 kid=arrData[parent*2+1]>arrData[parent*2+2]?parent*2+1:parent*2+2; 41 } 42 } 43 public: 44 MaxHeap():top(0){ arrData=new T[HEAP_SIZE]; } 45 ~MaxHeap(){ delete arrData; } 46 47 ///////////////////////////////////////////// 48 ////// Constractor & Destructor Above 49 ///////////////////////////////////////////// 50 bool insert(T v){ 51 if(top<HEAP_SIZE){ 52 arrData[top]=v; 53 top++; 54 FilterUp(); 55 return true; 56 }else return false; 57 } 58 59 T remove(){ 60 if(top){ 61 int iTop=arrData[0]; 62 arrData[0]=arrData[top-1]; 63 top--; 64 FilterDown(); 65 return iTop; 66 }else return 0; 67 } 68 }; 69 70 ////////////////////////////////// 71 template<typename T> 72 class MinHeap{ 73 T*arrData; 74 int top; 75 ////////////Filters Up&Down 76 77 void swap(T*l,T*r){ 78 T temp=*l; 79 *l=*r; 80 *r=temp; 81 } 82 83 ////////////////////////////////////////// 84 void FilterUp(){ 85 int kid=top-1; 86 int parent=(kid-1)/2; 87 while(arrData[kid]<arrData[parent]){ 88 swap(&arrData[kid],&arrData[parent]); 89 kid=parent; 90 parent=(parent-1)/2; 91 } 92 } 93 ///////////////////////////////////////// 94 void FilterDown(){ 95 int parent=0; 96 int kid=arrData[parent*2+1]<arrData[parent*2+2]?parent*2+1:parent*2+2; 97 while(kid<top&&arrData[kid]<arrData[parent]){ 98 swap(&arrData[kid],&arrData[parent]); 99 parent=kid; 100 kid=arrData[parent*2+1]<arrData[parent*2+2]?parent*2+1:parent*2+2; 101 } 102 } 103 ////////////////////////////////////////////////////// 104 public: 105 MinHeap():top(0){ arrData=new T[HEAP_SIZE]; } 106 ~MinHeap(){ delete arrData; } 107 108 ///////////////////////////////////////////// 109 ////// Constractor & Destructor Above 110 ///////////////////////////////////////////// 111 112 bool insert(T v){ 113 if(top<HEAP_SIZE){ 114 arrData[top]=v; 115 top++; 116 FilterUp(); 117 return true; 118 }else return false; 119 } 120 121 T remove(){ 122 if(top){ 123 int iTop=arrData[0]; 124 arrData[0]=arrData[top-1]; 125 top--; 126 FilterDown(); 127 return iTop; 128 }else return 0; 129 } 130 }; 131 132 class BridgeCrossing{ 133 134 void init(vector<int> v){ 135 for(int i=0;i<v.size();i++){ 136 here.insert(v[i]); 137 here2.insert(v[i]); 138 } 139 } 140 141 public: 142 MinHeap<int> here; 143 MinHeap<int> there; 144 MaxHeap<int> here2; 145 int minTime(vector<int> times){ 146 int iTotal=0; 147 init(times); 148 int max1st=-1; 149 int max2nd=-1; 150 int min1st=here.remove(); 151 int min2nd=here.remove(); 152 //////Returning Back 153 there.insert(min1st); 154 here.insert(min2nd); 155 while(true){ 156 max1st=here2.remove(); 157 if(max1st==min2nd)break; 158 max2nd=here2.remove(); 159 if(max2nd==min2nd)break; 160 iTotal+=max1st+min1st+2*min2nd; 161 } 162 if(max1st==min2nd){ 163 iTotal+=min2nd; 164 return iTotal; 165 }else if(max2nd==min2nd){ 166 iTotal+=min2nd+min1st+max1st; 167 return iTotal; 168 }else return -1; 169 } 170 };