增加一个读者计数器rc,设置初始值为0; 读者:Repeat P(mutex); readcount:=readcount+1; if readcount=1 then P (w); V(mutex);//mutex为互斥信号量,初始值为1 读 P(mutex); readcount:=readcount-1; if readcount=0 then V(w); V(mutex); Until false
滚入,滚出(Roll out, roll in )—交换由于基于优先级的算法而不同,低优先级的进程被换出,这样高优先级的进程可以被装入和执行。 交换时间的主要部分是转移时间,总的转移时间直接同交换的内存的数量成比例。 在许多系统如:UNIX,Linux,Windows中,可以找到一些被修正过的交换措施。 系统维持一个就绪队列,它包括在备份存储或在内存中准备运行的所有进程
Available: 长度为 m的向量。 如果available[j]=k,那么资源Rj有k个实例有效 Max: n x m 矩阵。 如果Max[i,j]=k,那么进程Pi最多可以请求k个资源Rj的实例 Allocation: n x m 矩阵。 如果Allocation[i,j]=k,那么进程Pj当前分配了k个资源Rj的实例 Need: n x m 矩阵。如果Need[,j]=k,那么进程Pj还需要k个资源Rj的实例 Need [i,j] = Max[i,j] – Allocation [i,j]. 1.让Work和Finish作为长度为m和n的向量初始化: Work := Available Finish [i] = false for i - 1,2,3, …, n. 2. 查找i (a) Finish [i] = false (b) Needi £ Work If no such i exists, go to step 4. 3. Work := Work + Allocationi Finish[i] := true go to step 2. 4. 如果对所有i的 Finish [i] = true, 则系统处在安全状态。
Requesti =进程 Pi 的资源请求向量. 如果Requesti [m] = k 则进程 Pi 想要资源类型为Rjm的k个实例 1. 如果 Requesti £ Needi 转 step 2. 否则报错, 因为进程请求超出了其声明的最大值 2. 如果 Requesti £ Available, 转 step 3. 否则 Pi 必须等待, 因为资源不可用. 3. 假设通过修改下列状态来分配请求的资源给进程Pi : Available := Available - Requesti; Allocationi := Allocationi + Requesti; Needi := Needi – Requesti;; •如果系统安全 Þ 将资源分配给 Pi. •如果系统不安全 Þ Pi 必须等待,恢复原有的资源分配状态
死锁检测和恢复
每个资源类型有一个实例:维护进程等待图 每个资源类型有多个实例:用available和finished数组探查是否死锁 允许进入死锁状态并加以恢复 维护等待图 节点是进程 Pi->Pj表明Pi在等待Pj 定期调用算法来检查是否有环 一个检查图中是否有环的算法需要n^2的操作来进行,n为图中的节点数 Available :一个长度为m的向量,表示每一种资源类型可用的实例数目 Allocation: 一个n x m 的矩阵,定义了当前分配的每一种资源类型的实例数目 Request: 一个n x m 的矩阵,表明了当前的进程请求。如果Request[i,j]=k,那么进程Pi请求k个资源Rj的实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14
1. 让Work和Finish作为长度为m和n的向量初始化 (a) Work = Available (b) For i = 0,2, …, n-1, if Allocationi ¹ 0, thenFinish[i] = false;otherwise, Finish[i] = true.
v用网络使得远程计算机之间的联系成为可能 手动传输文件如 FTP 自动,直接访问文件用分布文件系统 distributed file systems 半自动用万维网 world wide web vClient-server 客户机-服务器模型允许客户机登录远程服务器的文件系统 服务器可以服务多台客户机 识别客户可能是不安全和复杂的 NFS 是标准 UNIX 下客户机-服务器的文件共享协议 CIFS 是Windows下标准协议 标准操作系统文件调用翻译为远程调用 v分布式信息系统 (分布式命名服务 distributed naming services) 如LDAP, DNS, NIS。
故障
v所有文件系统都有故障模式 例如目录结构或者其他磁盘管理信息(元数据 metadata 损坏。 v远程文件系统加入新的故障模式,来自网络故障或者服务器故障 v从故障中恢复包含维护状态信息 state information vStateless 无状态协议如NFC在每个请求里包含所有信息,允许较为容易的故障恢复但是不够安全
共享
v规定系统的多个用户如何访问共享文件 类似于第六章的进程同步算法 v由于磁盘和网络的巨大延迟和很慢的传输速率,倾向于没这么复杂 Andrew File System (AFS) 实现了复杂共享语义 Unix file system (UFS) 使用: v一个用户对已打开文件的写入,对于打开同一文件的其他用户立即可见。 v一种共享模式允许用户共享文件的当前位置指针。 AFS 有会话语义 v一旦文件关闭,对其所作的更改只能被后来打开的会话可见。
访问控制权限和分组
v访问模式:读/写/执行 v三种类型的用户 RWX a) 所有者 7 =>1 1 1 RWX b) 组用户 6 => 1 1 0 RWX c) 公共用户 1 => 0 0 1 v建立一个组,加入一些用户 v对特定的文件或目录(game) ,定义适当的访问权限
增加一个读者计数器rc,设置初始值为0; 读者:Repeat P(mutex); readcount:=readcount+1; if readcount=1 then P (w); V(mutex);//mutex为互斥信号量,初始值为1 读 P(mutex); readcount:=readcount-1; if readcount=0 then V(w); V(mutex); Until false
滚入,滚出(Roll out, roll in )—交换由于基于优先级的算法而不同,低优先级的进程被换出,这样高优先级的进程可以被装入和执行。 交换时间的主要部分是转移时间,总的转移时间直接同交换的内存的数量成比例。 在许多系统如:UNIX,Linux,Windows中,可以找到一些被修正过的交换措施。 系统维持一个就绪队列,它包括在备份存储或在内存中准备运行的所有进程
Available: 长度为 m的向量。 如果available[j]=k,那么资源Rj有k个实例有效 Max: n x m 矩阵。 如果Max[i,j]=k,那么进程Pi最多可以请求k个资源Rj的实例 Allocation: n x m 矩阵。 如果Allocation[i,j]=k,那么进程Pj当前分配了k个资源Rj的实例 Need: n x m 矩阵。如果Need[,j]=k,那么进程Pj还需要k个资源Rj的实例 Need [i,j] = Max[i,j] – Allocation [i,j]. 1.让Work和Finish作为长度为m和n的向量初始化: Work := Available Finish [i] = false for i - 1,2,3, …, n. 2. 查找i (a) Finish [i] = false (b) Needi £ Work If no such i exists, go to step 4. 3. Work := Work + Allocationi Finish[i] := true go to step 2. 4. 如果对所有i的 Finish [i] = true, 则系统处在安全状态。
Requesti =进程 Pi 的资源请求向量. 如果Requesti [m] = k 则进程 Pi 想要资源类型为Rjm的k个实例 1. 如果 Requesti £ Needi 转 step 2. 否则报错, 因为进程请求超出了其声明的最大值 2. 如果 Requesti £ Available, 转 step 3. 否则 Pi 必须等待, 因为资源不可用. 3. 假设通过修改下列状态来分配请求的资源给进程Pi : Available := Available - Requesti; Allocationi := Allocationi + Requesti; Needi := Needi – Requesti;; •如果系统安全 Þ 将资源分配给 Pi. •如果系统不安全 Þ Pi 必须等待,恢复原有的资源分配状态
死锁检测和恢复
每个资源类型有一个实例:维护进程等待图 每个资源类型有多个实例:用available和finished数组探查是否死锁 允许进入死锁状态并加以恢复 维护等待图 节点是进程 Pi->Pj表明Pi在等待Pj 定期调用算法来检查是否有环 一个检查图中是否有环的算法需要n^2的操作来进行,n为图中的节点数 Available :一个长度为m的向量,表示每一种资源类型可用的实例数目 Allocation: 一个n x m 的矩阵,定义了当前分配的每一种资源类型的实例数目 Request: 一个n x m 的矩阵,表明了当前的进程请求。如果Request[i,j]=k,那么进程Pi请求k个资源Rj的实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14
1. 让Work和Finish作为长度为m和n的向量初始化 (a) Work = Available (b) For i = 0,2, …, n-1, if Allocationi ¹ 0, thenFinish[i] = false;otherwise, Finish[i] = true.
v用网络使得远程计算机之间的联系成为可能 手动传输文件如 FTP 自动,直接访问文件用分布文件系统 distributed file systems 半自动用万维网 world wide web vClient-server 客户机-服务器模型允许客户机登录远程服务器的文件系统 服务器可以服务多台客户机 识别客户可能是不安全和复杂的 NFS 是标准 UNIX 下客户机-服务器的文件共享协议 CIFS 是Windows下标准协议 标准操作系统文件调用翻译为远程调用 v分布式信息系统 (分布式命名服务 distributed naming services) 如LDAP, DNS, NIS。
故障
v所有文件系统都有故障模式 例如目录结构或者其他磁盘管理信息(元数据 metadata 损坏。 v远程文件系统加入新的故障模式,来自网络故障或者服务器故障 v从故障中恢复包含维护状态信息 state information vStateless 无状态协议如NFC在每个请求里包含所有信息,允许较为容易的故障恢复但是不够安全
共享
v规定系统的多个用户如何访问共享文件 类似于第六章的进程同步算法 v由于磁盘和网络的巨大延迟和很慢的传输速率,倾向于没这么复杂 Andrew File System (AFS) 实现了复杂共享语义 Unix file system (UFS) 使用: v一个用户对已打开文件的写入,对于打开同一文件的其他用户立即可见。 v一种共享模式允许用户共享文件的当前位置指针。 AFS 有会话语义 v一旦文件关闭,对其所作的更改只能被后来打开的会话可见。
访问控制权限和分组
v访问模式:读/写/执行 v三种类型的用户 RWX a) 所有者 7 =>1 1 1 RWX b) 组用户 6 => 1 1 0 RWX c) 公共用户 1 => 0 0 1 v建立一个组,加入一些用户 v对特定的文件或目录(game) ,定义适当的访问权限
set(集合):无序不允许重复的容器类,可以添加删除元素 You can add a value to a Set by writing set += value; s. ● You can remove a value from a Set by writing set -= value; ● You can check if a value exists in a Set by writing set.contains(value)map(键值对的集合) 如果没有对应key的value,返回默认值(见定义文件) `vector vector的remove根据移除元素的索引有1-n的复杂度,移除尾部为O(1),如果不在意索引,可以交换要移除元素和尾部元素再移除
● 向前走——如有必要,环绕——直到物品或空槽被放置 成立。 如果找到该项目,请将其删除。 然后,继续前进——包裹 around as necessary – 将表中的元素向后移动一个槽位,直到 找到空插槽或位于其原始位置的项目
string类
str::npos表示容器的最后一个成员位置 if (s.find("e") != string::npos) //find函数找不到时返回npos if s in str: string obj; obj.substr(int pos) //pos为要包含的第一个字符串的位置 std::string a = "0123456789abcdefghij";
/* Given a Vector<int>, returns the largest number in that Vector. */ int maxOf(const Vector<int>& values) { /* Bounds-check inputs. */ if (values.isEmpty()) { error("Can't find the maximum of no values."); } int result = values[0]; for (int i = 1; i < values.size(); i++) { result = max(result, values[i]); } return result; }
/* Given a list of numbers, creates a histogram from those numbers. */ Vector<int> histogramFor(const Vector<int>& values) { /* Create a histogram with the right number of slots. Initially, all values * in the histogram will be zero. */ Vector<int> histogram(maxOf(values) + 1); /* Scan across the input vector, incrementing the histogram values. */ for (int value: values) { histogram[value]++; } return histogram; }
void countingSort(Vector<int>& values) { /* Edge Case: If the array is empty, then it's already sorted. This is * needed because we can't take the maximum value of an empty vector. */ if (values.isEmpty()) { return; } /* Form the histogram. */ auto histogram = histogramFor(values); /* Scan across the histogram writing out the appropriate number of copies * of each value. We track the index of the next free spot to write to, * as it varies based on how many items we've written out so far. */ int next = 0; for (int value = 0; value < histogram.size(); value++) { /* Write out the right number of copies. */ for (int copy = 0; copy < histogram[value]; copy++) { values[next] = value; next++; } } }
/* Base case: There’s only one tree of size 0, namely, the empty BST. */ if (n == 0) return 1; /* Recursive case: Imagine all possible ways to choose a root and build the * left and right subtrees. */ int result = 0; /* Put the the nodes at indices 0, 1, 2, ..., n-1 up at the root. */ for (int i = 0; i < n; i++) { /* Each combination of a BST of i elements and a BST of n - 1 - i elements * can be used to build one BST of n elements. The number of pairs of * trees we can make this way is given by the product of the number of * trees of each type. */ result += numBSTsOfSize(i) * numBSTsOfSize(n - 1 - i); } return result; }
递归解决吃巧克力问题
求出吃法数量
1 2 3 4 5 6 7 8 9 10 11 12
if (numSquares<0) { error("输入数据不能为负数"); } else if (numSquares<=1) { return 1; } else { return numWaysToEat(numSquares-1)+numWaysToEat(numSquares-2); }
/* Print all ways to eat numSquares more squares, given that we've * already taken the bites given in soFar. */ void printWaysToEatRec(int numSquares, const Vector<int>& soFar) { /* Base Case: If there are no squares left, the only option is to use * the bites we've taken already in soFar. */ if (numSquares == 0) { cout << soFar << endl; } /* Base Case: If there is one square lfet, the only option is to eat * that square. */ else if (numSquares == 1) { cout << soFar + 1 << endl; } /* Otherwise, we take take bites of size one or of size two. */ else { printWaysToEatRec(numSquares - 1, soFar + 1); printWaysToEatRec(numSquares - 2, soFar + 2); } }
void printWaysToEat(int numSquares) { if (numSquares < 0) { error("You owe me some chocolate!"); }
/* We begin without having made any bites. */ printWaysToEatRec(numSquares, {}); }
bool isSorted(Stack<double> pancakes) { double last = -1; // No pancakes have negative size;
while (!pancakes.isEmpty()) { /* Check the next pancake. */ double next = pancakes.pop(); if (next < last) { return false; }
last = next; }
/* Pancakes are in increasing order! */ return true; }
/* Given a stack of pancakes and a flip size, flips that many pancakes * on the top of the stack. */ Stack<double> flip(Stack<double> pancakes, int numToFlip) { /* Take the top pancakes off the stack and run them into a queue. * This preserves the order in which they were removed. */ Queue<double> buffer; for (int i = 0; i < numToFlip; i++) { buffer.enqueue(pancakes.pop()); }
/* Move the pancakes back. */ while (!buffer.isEmpty()) { pancakes.push(buffer.dequeue()); }
return pancakes; }
Optional<Vector<int>> sortStack(Stack<double> pancakes, int numFlips) { /* Base Case: If the stack is sorted, great! We're done, and no flips * were needed. */ if (isSorted(pancakes)) { return { }; // No flips } /* Base Case: If the stack isn't sorted and we're out of flips, then * there is no way to sort things. */ else if (numFlips == 0) { return Nothing; } /* Recursive Case: The stack isn't sorted and we still have flips left. * The next flip could flip 1, 2, 3, ..., or all N of the pancakes. * Try each option and see whether any of them work. */ for (int numToFlip = 1; numToFlip <= pancakes.size(); numToFlip++) { /* Make the flip and see if it works. */ auto result = sortStack(flip(pancakes, numToFlip), numFlips - 1); if (result != Nothing) { /* The result holds all the remaining flips but doesn't know about * the flip we just did. Insert that flip at the beginning. */ result.value().insert(0, numToFlip); return result; } }
/* If we're here, then no matter which flip we make first, we cannot * get the pancakes sorted. Give up. */ return Nothing; }
递归解决天平问题
1 2 3 4 5 6 7 8 9 10 11 12 13
bool isMeasurableRec(int amount, const Vector<int>& weights, int index) { if (index == weights.size()) { return amount == 0; } else { return isMeasurableRec(amount, weights, index + 1) || isMeasurableRec(amount + weights[index], weights, index + 1) || isMeasurableRec(amount - weights[index], weights, index + 1); } }
int fewestCoinsFor(int cents, const Set<int>& coins) { /* Can't have a negative number of cents. */ if (cents < 0) { error("You owe me money, not the other way around!"); } /* Base case: You need no coins to give change for no cents. */ else if (cents == 0) { return 0; } /* Base case: No coins exist. Then it's not possible to make the * amount. In that case, give back a really large value as a * sentinel. */ else if (coins.isEmpty()) { return cents + 1; } /* Recursive case: Pick a coin, then try using each distinct number of * copies of it that we can. */ else { /* The best we've found so far. We initialize this to a large value so * that it's replaced on the first iteration of the loop. Do you see * why cents + 1 is a good choice? */ int bestSoFar = cents + 1; /* Pick a coin. */ int coin = coins.first(); /* Try all amounts of it. */ for (int copies = 0; copies * coin <= cents; copies++) { /* See what happens if we make this choice. Once we use this * coin, we won't use the same coin again in the future. */ int thisChoice = copies + fewestCoinsFor(cents - copies * coin, coins - coin); /* Is this better than what we have so far? */ if (thisChoice < bestSoFar) { bestSoFar = thisChoice; } } /* Return whatever worked best. */ return bestSoFar; } }
/* How few coins are needed to make the total, given that we can only use * coins from index startIndex and onward? */ int fewestCoinsRec(int cents, const Vector<int>& coins, int startIndex,Grid<int>& memo) { /* Base case: You need no coins to give change for no cents. */ if (cents == 0) { return 0; } /* Base case: No coins exist. Then it's not possible to make the * amount. In that case, give back a really large value as a * sentinel. */ else if (startIndex == coins.size()) { return cents + 1; } /* Base case: We already know the answer. */ else if (memo[cents][startIndex] != -1) { return memo[cents][startIndex]; } /* Recursive case: Pick a coin, then try using each distinct number of * copies of it that we can. */ else { /* The best we've found so far. We initialize this to a large value so * that it's replaced on the first iteration of the loop. Do you see * why cents + 1 is a good choice? */ int bestSoFar = cents + 1;
/* Pick a coin. */ int coin = coins[startIndex];
/* Try all amounts of it. */ for (int copies = 0; copies * coin <= cents; copies++) { /* See what happens if we make this choice. Once we use this * coin, we won't use the same coin again in the future. */ int thisChoice = copies + fewestCoinsRec(cents - copies * coin, coins, startIndex + 1, memo);
/* Is this better than what we have so far? */ if (thisChoice < bestSoFar) { bestSoFar = thisChoice; } }
int fewestCoinsFor(int cents, const Set<int>& coins) { /* Can't have a negative number of cents. */ if (cents < 0) { error("You owe me money, not the other way around!"); }
/* Convert from a Set<int> to a Vector<int> so we have a nice ordering * on things. */ Vector<int> coinVec; for (int coin: coins) { coinVec += coin; }
/* Build our memoization table. Since the number of cents left ranges from * 0 to cents, we need cents+1 rows. Since the start index of the coin * ranges from 0 to coins.size(), we make coins.size() + 1 columns. * * -1 is used as a sentinel to indicate "nothing has been computed here * yet." */ Grid<int> memo(cents + 1, coins.size() + 1, -1);
/* Now ask how many coins are needed to make the total, using any coins * from index 0 onward. */ return fewestCoinsRec(cents, coinVec, 0, memo); }
void listPossiblePaymentsRec(int total, const Set<string>& people,const Map<string, int>& payments) { /* Base case: if there's one person left, they have to pay the whole bill. */ if (people.size() == 1) { Map<string, int> finalPayments = payments; finalPayments[people.first()] = total; cout << finalPayments << endl; } /* Recursive case: The first person has to pay some amount between 0 and the * total amount. Try all of those possibilities. */ else { for (int payment = 0; payment <= total; payment++) { /* Create a new assignment of people to payments in which this first * person pays this amount. */ Map<string, int> updatedPayments = payments; updatedPayments[people.first()] = payment; listPossiblePaymentsRec(total - payment, people - people.first(),updatedPayments); } } } void listPossiblePayments(int total, const Set<string>& people) { /* Edge cases: we can't pay a negative total, and there must be at least one * person. */ if (total < 0) error("Guess you're an employee?"); if (people.isEmpty()) error("Dine and dash?"); listPossiblePaymentsRec(total, people, {}); }
Optional<Vector<int>> findSquareSequence(int n) { /*Validate input.*/ if (n < 0) { error("Don't be so negative!"); }
/* Build a set of the numbers 1, 2, 3, ..., n. */ Set<int> options; for (int i = 1; i <= n; i++) { options += i; } return findSequenceRec(options, { }); }
Optional<Vector<int>> findSequenceRec(const Set<int>& unused, const Vector<int>& soFar) { /*Base Case: If all numbers are used, we have our sequence!*/ if (unused.isEmpty()) { return soFar; }
/* Recursive Case: Some number comes next. Try each of them and see which * one we should pick. */ for (int next: unused) { /* We can use this if either * * 1. the sequence is empty, so we're first in line, or * 2. the sequence is not empty, but we sum to a perfect square * with the previous term. */ if (soFar.isEmpty() || isPerfectSquare(next + soFar[soFar.size() - 1])) { /* See what happens if we extend with this number. */ auto result = findSequenceRec(unused - next, soFar + next); if (result != Nothing) { return result; } } }
/* Tried all options and none of them worked. Oh well! */ return Nothing; }
set(集合):无序不允许重复的容器类,可以添加删除元素 You can add a value to a Set by writing set += value; s. ● You can remove a value from a Set by writing set -= value; ● You can check if a value exists in a Set by writing set.contains(value)map(键值对的集合) 如果没有对应key的value,返回默认值(见定义文件) `vector vector的remove根据移除元素的索引有1-n的复杂度,移除尾部为O(1),如果不在意索引,可以交换要移除元素和尾部元素再移除
● 向前走——如有必要,环绕——直到物品或空槽被放置 成立。 如果找到该项目,请将其删除。 然后,继续前进——包裹 around as necessary – 将表中的元素向后移动一个槽位,直到 找到空插槽或位于其原始位置的项目
string类
str::npos表示容器的最后一个成员位置 if (s.find("e") != string::npos) //find函数找不到时返回npos if s in str: string obj; obj.substr(int pos) //pos为要包含的第一个字符串的位置 std::string a = "0123456789abcdefghij";
/* Given a Vector<int>, returns the largest number in that Vector. */ int maxOf(const Vector<int>& values) { /* Bounds-check inputs. */ if (values.isEmpty()) { error("Can't find the maximum of no values."); } int result = values[0]; for (int i = 1; i < values.size(); i++) { result = max(result, values[i]); } return result; }
/* Given a list of numbers, creates a histogram from those numbers. */ Vector<int> histogramFor(const Vector<int>& values) { /* Create a histogram with the right number of slots. Initially, all values * in the histogram will be zero. */ Vector<int> histogram(maxOf(values) + 1); /* Scan across the input vector, incrementing the histogram values. */ for (int value: values) { histogram[value]++; } return histogram; }
void countingSort(Vector<int>& values) { /* Edge Case: If the array is empty, then it's already sorted. This is * needed because we can't take the maximum value of an empty vector. */ if (values.isEmpty()) { return; } /* Form the histogram. */ auto histogram = histogramFor(values); /* Scan across the histogram writing out the appropriate number of copies * of each value. We track the index of the next free spot to write to, * as it varies based on how many items we've written out so far. */ int next = 0; for (int value = 0; value < histogram.size(); value++) { /* Write out the right number of copies. */ for (int copy = 0; copy < histogram[value]; copy++) { values[next] = value; next++; } } }
/* Base case: There’s only one tree of size 0, namely, the empty BST. */ if (n == 0) return 1; /* Recursive case: Imagine all possible ways to choose a root and build the * left and right subtrees. */ int result = 0; /* Put the the nodes at indices 0, 1, 2, ..., n-1 up at the root. */ for (int i = 0; i < n; i++) { /* Each combination of a BST of i elements and a BST of n - 1 - i elements * can be used to build one BST of n elements. The number of pairs of * trees we can make this way is given by the product of the number of * trees of each type. */ result += numBSTsOfSize(i) * numBSTsOfSize(n - 1 - i); } return result; }
递归解决吃巧克力问题
求出吃法数量
1 2 3 4 5 6 7 8 9 10 11 12
if (numSquares<0) { error("输入数据不能为负数"); } else if (numSquares<=1) { return 1; } else { return numWaysToEat(numSquares-1)+numWaysToEat(numSquares-2); }
/* Print all ways to eat numSquares more squares, given that we've * already taken the bites given in soFar. */ void printWaysToEatRec(int numSquares, const Vector<int>& soFar) { /* Base Case: If there are no squares left, the only option is to use * the bites we've taken already in soFar. */ if (numSquares == 0) { cout << soFar << endl; } /* Base Case: If there is one square lfet, the only option is to eat * that square. */ else if (numSquares == 1) { cout << soFar + 1 << endl; } /* Otherwise, we take take bites of size one or of size two. */ else { printWaysToEatRec(numSquares - 1, soFar + 1); printWaysToEatRec(numSquares - 2, soFar + 2); } }
void printWaysToEat(int numSquares) { if (numSquares < 0) { error("You owe me some chocolate!"); }
/* We begin without having made any bites. */ printWaysToEatRec(numSquares, {}); }
bool isSorted(Stack<double> pancakes) { double last = -1; // No pancakes have negative size;
while (!pancakes.isEmpty()) { /* Check the next pancake. */ double next = pancakes.pop(); if (next < last) { return false; }
last = next; }
/* Pancakes are in increasing order! */ return true; }
/* Given a stack of pancakes and a flip size, flips that many pancakes * on the top of the stack. */ Stack<double> flip(Stack<double> pancakes, int numToFlip) { /* Take the top pancakes off the stack and run them into a queue. * This preserves the order in which they were removed. */ Queue<double> buffer; for (int i = 0; i < numToFlip; i++) { buffer.enqueue(pancakes.pop()); }
/* Move the pancakes back. */ while (!buffer.isEmpty()) { pancakes.push(buffer.dequeue()); }
return pancakes; }
Optional<Vector<int>> sortStack(Stack<double> pancakes, int numFlips) { /* Base Case: If the stack is sorted, great! We're done, and no flips * were needed. */ if (isSorted(pancakes)) { return { }; // No flips } /* Base Case: If the stack isn't sorted and we're out of flips, then * there is no way to sort things. */ else if (numFlips == 0) { return Nothing; } /* Recursive Case: The stack isn't sorted and we still have flips left. * The next flip could flip 1, 2, 3, ..., or all N of the pancakes. * Try each option and see whether any of them work. */ for (int numToFlip = 1; numToFlip <= pancakes.size(); numToFlip++) { /* Make the flip and see if it works. */ auto result = sortStack(flip(pancakes, numToFlip), numFlips - 1); if (result != Nothing) { /* The result holds all the remaining flips but doesn't know about * the flip we just did. Insert that flip at the beginning. */ result.value().insert(0, numToFlip); return result; } }
/* If we're here, then no matter which flip we make first, we cannot * get the pancakes sorted. Give up. */ return Nothing; }
递归解决天平问题
1 2 3 4 5 6 7 8 9 10 11 12 13
bool isMeasurableRec(int amount, const Vector<int>& weights, int index) { if (index == weights.size()) { return amount == 0; } else { return isMeasurableRec(amount, weights, index + 1) || isMeasurableRec(amount + weights[index], weights, index + 1) || isMeasurableRec(amount - weights[index], weights, index + 1); } }
int fewestCoinsFor(int cents, const Set<int>& coins) { /* Can't have a negative number of cents. */ if (cents < 0) { error("You owe me money, not the other way around!"); } /* Base case: You need no coins to give change for no cents. */ else if (cents == 0) { return 0; } /* Base case: No coins exist. Then it's not possible to make the * amount. In that case, give back a really large value as a * sentinel. */ else if (coins.isEmpty()) { return cents + 1; } /* Recursive case: Pick a coin, then try using each distinct number of * copies of it that we can. */ else { /* The best we've found so far. We initialize this to a large value so * that it's replaced on the first iteration of the loop. Do you see * why cents + 1 is a good choice? */ int bestSoFar = cents + 1; /* Pick a coin. */ int coin = coins.first(); /* Try all amounts of it. */ for (int copies = 0; copies * coin <= cents; copies++) { /* See what happens if we make this choice. Once we use this * coin, we won't use the same coin again in the future. */ int thisChoice = copies + fewestCoinsFor(cents - copies * coin, coins - coin); /* Is this better than what we have so far? */ if (thisChoice < bestSoFar) { bestSoFar = thisChoice; } } /* Return whatever worked best. */ return bestSoFar; } }
/* How few coins are needed to make the total, given that we can only use * coins from index startIndex and onward? */ int fewestCoinsRec(int cents, const Vector<int>& coins, int startIndex,Grid<int>& memo) { /* Base case: You need no coins to give change for no cents. */ if (cents == 0) { return 0; } /* Base case: No coins exist. Then it's not possible to make the * amount. In that case, give back a really large value as a * sentinel. */ else if (startIndex == coins.size()) { return cents + 1; } /* Base case: We already know the answer. */ else if (memo[cents][startIndex] != -1) { return memo[cents][startIndex]; } /* Recursive case: Pick a coin, then try using each distinct number of * copies of it that we can. */ else { /* The best we've found so far. We initialize this to a large value so * that it's replaced on the first iteration of the loop. Do you see * why cents + 1 is a good choice? */ int bestSoFar = cents + 1;
/* Pick a coin. */ int coin = coins[startIndex];
/* Try all amounts of it. */ for (int copies = 0; copies * coin <= cents; copies++) { /* See what happens if we make this choice. Once we use this * coin, we won't use the same coin again in the future. */ int thisChoice = copies + fewestCoinsRec(cents - copies * coin, coins, startIndex + 1, memo);
/* Is this better than what we have so far? */ if (thisChoice < bestSoFar) { bestSoFar = thisChoice; } }
int fewestCoinsFor(int cents, const Set<int>& coins) { /* Can't have a negative number of cents. */ if (cents < 0) { error("You owe me money, not the other way around!"); }
/* Convert from a Set<int> to a Vector<int> so we have a nice ordering * on things. */ Vector<int> coinVec; for (int coin: coins) { coinVec += coin; }
/* Build our memoization table. Since the number of cents left ranges from * 0 to cents, we need cents+1 rows. Since the start index of the coin * ranges from 0 to coins.size(), we make coins.size() + 1 columns. * * -1 is used as a sentinel to indicate "nothing has been computed here * yet." */ Grid<int> memo(cents + 1, coins.size() + 1, -1);
/* Now ask how many coins are needed to make the total, using any coins * from index 0 onward. */ return fewestCoinsRec(cents, coinVec, 0, memo); }
void listPossiblePaymentsRec(int total, const Set<string>& people,const Map<string, int>& payments) { /* Base case: if there's one person left, they have to pay the whole bill. */ if (people.size() == 1) { Map<string, int> finalPayments = payments; finalPayments[people.first()] = total; cout << finalPayments << endl; } /* Recursive case: The first person has to pay some amount between 0 and the * total amount. Try all of those possibilities. */ else { for (int payment = 0; payment <= total; payment++) { /* Create a new assignment of people to payments in which this first * person pays this amount. */ Map<string, int> updatedPayments = payments; updatedPayments[people.first()] = payment; listPossiblePaymentsRec(total - payment, people - people.first(),updatedPayments); } } } void listPossiblePayments(int total, const Set<string>& people) { /* Edge cases: we can't pay a negative total, and there must be at least one * person. */ if (total < 0) error("Guess you're an employee?"); if (people.isEmpty()) error("Dine and dash?"); listPossiblePaymentsRec(total, people, {}); }
Optional<Vector<int>> findSquareSequence(int n) { /*Validate input.*/ if (n < 0) { error("Don't be so negative!"); }
/* Build a set of the numbers 1, 2, 3, ..., n. */ Set<int> options; for (int i = 1; i <= n; i++) { options += i; } return findSequenceRec(options, { }); }
Optional<Vector<int>> findSequenceRec(const Set<int>& unused, const Vector<int>& soFar) { /*Base Case: If all numbers are used, we have our sequence!*/ if (unused.isEmpty()) { return soFar; }
/* Recursive Case: Some number comes next. Try each of them and see which * one we should pick. */ for (int next: unused) { /* We can use this if either * * 1. the sequence is empty, so we're first in line, or * 2. the sequence is not empty, but we sum to a perfect square * with the previous term. */ if (soFar.isEmpty() || isPerfectSquare(next + soFar[soFar.size() - 1])) { /* See what happens if we extend with this number. */ auto result = findSequenceRec(unused - next, soFar + next); if (result != Nothing) { return result; } } }
/* Tried all options and none of them worked. Oh well! */ return Nothing; }