Loading docs/search/opt.md +51 −52 Original line number Diff line number Diff line Loading @@ -132,20 +132,19 @@ void dfs(传入数值) { **分析** 由于每个人都必须分配到工作,在这里可以建一个二维数组 `time[i][j]` ,用以表示 $i$ 个人完成 $j$ 号工作所花费的时间。给定一个循环,从第 1 个人开始循环分配工作,直到所有人都分配到。为第 $i$ 个人分配工作时,再循环检查每个工作是否已被分配,没有则分配给 $i$ 个人,否则检查下一个工作。可以用一个一维数组 `isWorked[j]` 来表示第 $j$ 号工作是否已被分配,未分配则 `isWorked[j]=0` ,否则 `isWorked[j]=1` 。利用回溯思想,在工人循环结束后回到上一工人,取消此次分配的工作,而去分配下一工作直到可以分配为止。这样,一直回溯到第 1 个工人后,就能得到所有的可行解。在检查工作分配时,其实就是判断取得可行解时的二维数组的第一维下标各不相同和第二维下标各不相同。而我们是要得到完成这 $n$ 份工作的最小时间总和,即可行解中和最小的一个,故需要再定义一个全局变量 `cost_time_total_min` 表示最终的时间总和,初始 `cost_time_total_min` 为 `time[i][i]` 之和,即对角线工作时间相加之和。在所有人分配完工作时,比较 $count$ 与 `cost_time_total_min` 的大小,如果 $count$ 小于 `cost_time_total_min` ,证明在回溯时找到了一个最优解,此时就把 $count$ 赋给 `cost_time_total_min` 。但考虑到算法的复杂度,这里还有一个剪枝优化的工作可以做。就是在每次计算局部费用变量 $count$ 的值时,如果判断 $count$ 已经大于 `cost_time_total_min` ,就没必要再往下分配了,因为这时得到的解必然不是最优解。 由于每个人都必须分配到工作,在这里可以建一个二维数组 `time[i][j]` ,用以表示 $i$ 个人完成 $j$ 号工作所花费的时间。给定一个循环,从第 1 个人开始循环分配工作,直到所有人都分配到。为第 $i$ 个人分配工作时,再循环检查每个工作是否已被分配,没有则分配给 $i$ 个人,否则检查下一个工作。可以用一个一维数组 `is_working[j]` 来表示第 $j$ 号工作是否已被分配,未分配则 `is_working[j]=0` ,否则 `is_working[j]=1` 。利用回溯思想,在工人循环结束后回到上一工人,取消此次分配的工作,而去分配下一工作直到可以分配为止。这样,一直回溯到第 1 个工人后,就能得到所有的可行解。在检查工作分配时,其实就是判断取得可行解时的二维数组的第一维下标各不相同和第二维下标各不相同。而我们是要得到完成这 $n$ 份工作的最小时间总和,即可行解中和最小的一个,故需要再定义一个全局变量 `cost_time_total_min` 表示最终的时间总和,初始 `cost_time_total_min` 为 `time[i][i]` 之和,即对角线工作时间相加之和。在所有人分配完工作时,比较 $count$ 与 `cost_time_total_min` 的大小,如果 $count$ 小于 `cost_time_total_min` ,证明在回溯时找到了一个最优解,此时就把 $count$ 赋给 `cost_time_total_min` 。但考虑到算法的复杂度,这里还有一个剪枝优化的工作可以做。就是在每次计算局部费用变量 $count$ 的值时,如果判断 $count$ 已经大于 `cost_time_total_min` ,就没必要再往下分配了,因为这时得到的解必然不是最优解。 #### 经典例题代码 ```c++ #include <cstdio> #define N 16 int isWorked[N] = {0}; // 某项工作是否被分配 int is_working[N] = {0};// 某项工作是否被分配 int time[N][N];// 完成某项工作所需的时间 int cost_time_total_min;// 完成 n 份工作的最小时间总和 // i 表示第几个人,count 表示工作费用总和 inline void work(int i, int count, int n){ // 如果 i 超出了所能分配的最大工作件数,表示分配完成,并且 count 比原来 // cost_time_total_min 花费少 则更新 cost_time_total_min 的值 // 如果 i 超出了所能分配的最大工作件数,表示分配完成,并且 count 比原来 cost_time_total_min 花费少 则更新 cost_time_total_min 的值 if(i > n && count < cost_time_total_min){ cost_time_total_min = count; return; Loading @@ -154,20 +153,20 @@ inline void work(int i, int count, int n) { if(count < cost_time_total_min){ // j 表示第几件工作 for(int j = 1 ; j <= n; j++){ // 如果工作未被分配 isWorked = 0 if (isWorked[j] == 0) { // 分配工作 isWorked = 1 isWorked[j] = 1; // 如果工作未被分配 is_working = 0 if(is_working[j] == 0){ // 分配工作 is_working = 1 is_working[j] = 1; //工作交给第 i + 1 个人 work(i + 1, count + time[i][j], n); //在一轮迭代完成之后,返回到上一个人,要对此次的工作进行重新分配,将 // isWorked[j] 重设为 0 isWorked[j] = 0; //在一轮迭代完成之后,返回到上一个人,要对此次的工作进行重新分配,将 is_working[j] 重设为 0 is_working[j] = 0; } } } } int main(int argc, char const *argv[]) { int main(int argc, char const *argv[]) { setvbuf(stdin, new char[1 << 20], _IOFBF, 1 << 20); setvbuf(stdout, new char[1 << 20], _IOFBF, 1 << 20); int n; Loading Loading
docs/search/opt.md +51 −52 Original line number Diff line number Diff line Loading @@ -132,20 +132,19 @@ void dfs(传入数值) { **分析** 由于每个人都必须分配到工作,在这里可以建一个二维数组 `time[i][j]` ,用以表示 $i$ 个人完成 $j$ 号工作所花费的时间。给定一个循环,从第 1 个人开始循环分配工作,直到所有人都分配到。为第 $i$ 个人分配工作时,再循环检查每个工作是否已被分配,没有则分配给 $i$ 个人,否则检查下一个工作。可以用一个一维数组 `isWorked[j]` 来表示第 $j$ 号工作是否已被分配,未分配则 `isWorked[j]=0` ,否则 `isWorked[j]=1` 。利用回溯思想,在工人循环结束后回到上一工人,取消此次分配的工作,而去分配下一工作直到可以分配为止。这样,一直回溯到第 1 个工人后,就能得到所有的可行解。在检查工作分配时,其实就是判断取得可行解时的二维数组的第一维下标各不相同和第二维下标各不相同。而我们是要得到完成这 $n$ 份工作的最小时间总和,即可行解中和最小的一个,故需要再定义一个全局变量 `cost_time_total_min` 表示最终的时间总和,初始 `cost_time_total_min` 为 `time[i][i]` 之和,即对角线工作时间相加之和。在所有人分配完工作时,比较 $count$ 与 `cost_time_total_min` 的大小,如果 $count$ 小于 `cost_time_total_min` ,证明在回溯时找到了一个最优解,此时就把 $count$ 赋给 `cost_time_total_min` 。但考虑到算法的复杂度,这里还有一个剪枝优化的工作可以做。就是在每次计算局部费用变量 $count$ 的值时,如果判断 $count$ 已经大于 `cost_time_total_min` ,就没必要再往下分配了,因为这时得到的解必然不是最优解。 由于每个人都必须分配到工作,在这里可以建一个二维数组 `time[i][j]` ,用以表示 $i$ 个人完成 $j$ 号工作所花费的时间。给定一个循环,从第 1 个人开始循环分配工作,直到所有人都分配到。为第 $i$ 个人分配工作时,再循环检查每个工作是否已被分配,没有则分配给 $i$ 个人,否则检查下一个工作。可以用一个一维数组 `is_working[j]` 来表示第 $j$ 号工作是否已被分配,未分配则 `is_working[j]=0` ,否则 `is_working[j]=1` 。利用回溯思想,在工人循环结束后回到上一工人,取消此次分配的工作,而去分配下一工作直到可以分配为止。这样,一直回溯到第 1 个工人后,就能得到所有的可行解。在检查工作分配时,其实就是判断取得可行解时的二维数组的第一维下标各不相同和第二维下标各不相同。而我们是要得到完成这 $n$ 份工作的最小时间总和,即可行解中和最小的一个,故需要再定义一个全局变量 `cost_time_total_min` 表示最终的时间总和,初始 `cost_time_total_min` 为 `time[i][i]` 之和,即对角线工作时间相加之和。在所有人分配完工作时,比较 $count$ 与 `cost_time_total_min` 的大小,如果 $count$ 小于 `cost_time_total_min` ,证明在回溯时找到了一个最优解,此时就把 $count$ 赋给 `cost_time_total_min` 。但考虑到算法的复杂度,这里还有一个剪枝优化的工作可以做。就是在每次计算局部费用变量 $count$ 的值时,如果判断 $count$ 已经大于 `cost_time_total_min` ,就没必要再往下分配了,因为这时得到的解必然不是最优解。 #### 经典例题代码 ```c++ #include <cstdio> #define N 16 int isWorked[N] = {0}; // 某项工作是否被分配 int is_working[N] = {0};// 某项工作是否被分配 int time[N][N];// 完成某项工作所需的时间 int cost_time_total_min;// 完成 n 份工作的最小时间总和 // i 表示第几个人,count 表示工作费用总和 inline void work(int i, int count, int n){ // 如果 i 超出了所能分配的最大工作件数,表示分配完成,并且 count 比原来 // cost_time_total_min 花费少 则更新 cost_time_total_min 的值 // 如果 i 超出了所能分配的最大工作件数,表示分配完成,并且 count 比原来 cost_time_total_min 花费少 则更新 cost_time_total_min 的值 if(i > n && count < cost_time_total_min){ cost_time_total_min = count; return; Loading @@ -154,20 +153,20 @@ inline void work(int i, int count, int n) { if(count < cost_time_total_min){ // j 表示第几件工作 for(int j = 1 ; j <= n; j++){ // 如果工作未被分配 isWorked = 0 if (isWorked[j] == 0) { // 分配工作 isWorked = 1 isWorked[j] = 1; // 如果工作未被分配 is_working = 0 if(is_working[j] == 0){ // 分配工作 is_working = 1 is_working[j] = 1; //工作交给第 i + 1 个人 work(i + 1, count + time[i][j], n); //在一轮迭代完成之后,返回到上一个人,要对此次的工作进行重新分配,将 // isWorked[j] 重设为 0 isWorked[j] = 0; //在一轮迭代完成之后,返回到上一个人,要对此次的工作进行重新分配,将 is_working[j] 重设为 0 is_working[j] = 0; } } } } int main(int argc, char const *argv[]) { int main(int argc, char const *argv[]) { setvbuf(stdin, new char[1 << 20], _IOFBF, 1 << 20); setvbuf(stdout, new char[1 << 20], _IOFBF, 1 << 20); int n; Loading