5-1 子集合问题
【问题描述】子集合问题的一个实例为<S, t>。其中,S={x1, x2, …, xn}是一个正整数的集合,c是一个正整数。子集合问题判定是否存在S的一个子集S1,使得sum(x ∈ S1) = c。试设计一个解子集合问题的回溯法。
【算法设计】对于给定的正整数的集合S={x1, x2, …, xn}和正整数c,计算S的一个子集S1,使得sum(x ∈ S1) = c。
【数据输入】由文件input.txt提供输入数据。文件第1行有2个正整数n和c,n表示S的大小,c是子集合的目标值。接下来的1行中,有n个正整数,表示集合S中的元素。
【结果输出】将子集合问题的解输出到文件output.txt。当问题无解时,输出“No Solution!”。
代码如下·:
#include <iostream>
#include <fstream>
using namespace std;
int n,c;
int* nums; // 动态分配存储集合
int* result; // 动态分配存储当前解
int resultSize=0; // 当前解的大小
bool found=false; // 是否找到解
// 回溯函数
void backtrack(int t,int currentSum){
if(found) return; // 如果已经找到解,直接返回
if(currentSum==c){
found=true;
ofstream outputFile("output.txt");
for(int i=0;i<resultSize;i++){
outputFile<<result[i]<<" ";
}
outputFile<<endl;
outputFile.close();
return;
}
for(int i=t;i<n;i++){
if(currentSum+nums[i]<=c){
result[resultSize++]=nums[i]; // 选择当前元素
backtrack(i+1,currentSum+nums[i]); // 递归
if(found) return; // 如果找到解,直接返回
resultSize--; // 回溯,取消选择
}
}
}
int main() {
ifstream inputFile("input.txt"); // 打开输入文件
ofstream outputFile("output.txt"); // 打开输出文件(初始化时清空内容)
outputFile.close();
inputFile>>n>>c;
nums=new int[n]; // 动态分配数组nums
result=new int[n]; // 动态分配数组result
int sum=0;
for(int i=0;i<n;i++){
inputFile>>nums[i];
sum+=nums[i];
}
inputFile.close();
if(sum<c){
ofstream outputFile("output.txt");
outputFile<<"No Solution!"<<endl; // 如果所有数字之和小于目标值,直接输出
outputFile.close();
}
else{
backtrack(0,0); // 调用回溯函数
if(!found){
ofstream outputFile("output.txt");
outputFile<<"No Solution!"<<endl;
outputFile.close(); // 关闭输出文件
}
}
delete[] nums; // 释放动态分配的nums数组
delete[] result; // 释放动态分配的result数组
return 0;
}
// 运行完成后打开output.txt文件即可看到结果
5-9 拉丁矩阵问题
【问题描述】现有 n 种不同形状的宝石,每种宝石有足够多颗。欲将这些宝石排列成 m 行 n 列的一个矩阵,m ≤ n,使矩阵中每行和每列的宝石都没有相同形状。试设计一个算法,计算出对于给定的 m 和 n,有多少种不同的宝石排列方案。
【算法设计】对于给定的 m 和 n,计算出不同的宝石排列方案数。
【数据输入】由文件 input.txt 给出输入数据。第 1 行有 2 个正整数 m 和 n(0 < m ≤ n ≤ 9)。
【结果输出】将计算的宝石排列方案数输出到文件 output.txt。
代码如下:
#include <iostream>
#include <fstream>
using namespace std;
int m,n; // 矩阵的行数和列数
int countSolutions=0; // 解的总数
int** matrix; // 动态分配矩阵,用于存储当前排列状态
// 检查是否符合列约束
bool isValid(int x,int y){
for(int i=0;i<x;i++){
if(matrix[i][y]==matrix[x][y]) { // 检查列中是否有重复元素
return false;
}
}
return true;
}
// 回溯函数,排列并递归检查
void traceback(int x,int y){
int temp;
// 尝试在当前行中进行排列
for(int i=y;i<n;i++){
// 交换列中的元素
temp=matrix[x][y];
matrix[x][y]=matrix[x][i];
matrix[x][i]=temp;
if (isValid(x, y)) { // 如果列中没有重复元素
if((x==m-1)&&(y==n-1)){ // 如果到达最后一个单元格,计数加一
countSolutions++;
}
else if((x!=m-1)&&(y==n-1)){ // 如果是最后一列,但不是最后一行
traceback(x + 1, 0);
}
else { // 其他情况,继续递归到下一列
traceback(x,y+1);
}
}
// 回溯,恢复原始排列
temp=matrix[x][y];
matrix[x][y]=matrix[x][i];
matrix[x][i]=temp;
}
}
int main(){
ifstream inputFile("input.txt");
ofstream outputFile("output.txt");
inputFile>>m>>n;
inputFile.close();
// 动态分配矩阵
matrix=new int*[m];
for(int i=0;i<m;i++){
matrix[i]=new int[n];
for(int j=0;j<n;j++){
matrix[i][j]=j+1; // 初始化为1到n
}
}
// 开始回溯搜索
traceback(0,0);
outputFile<<countSolutions<<endl;
outputFile.close();
// 释放动态分配的内存
for(int i=0;i<m;i++){
delete[] matrix[i];
}
delete[] matrix;
return 0;
}
// 运行完成后打开output.txt文件即可看到结果
5-13 工作分配问题
【问题描述】有 n 件工作分配给 n 个人。将工作 i 分配给第 j 个人所需的费用为 cij。试设计一个算法,为每个人都分配 1 件不同的工作,并使总费用达到最小。
【算法设计】设计一个算法,对于给定的工作费用,计算最佳工作分配方案,使总费用达到最小。
【数据输入】由文件 input.txt 给出输入数据。第 1 行有 1 个正整数 n(1≤n≤20)。接下来的 n 行,每行 n 个数,表示工作费用。
【结果输出】将计算的最小总费用输出到文件 output.txt。
代码如下:
#include <iostream>
#include <climits>
#include <fstream>
using namespace std;
int n;
int c[21][21]; // cij为将工作i分配给第j个人的费用
int mincost=INT_MAX; // 最小工作费用和
int sum=0; // 当前工作费用和
int x[21]; // 保存工作分配的顺序
// 回溯函数
void backtrack(int t){ // 为工作t进行分配
if(t>n){ // 如果所有工作分配完成
if(sum<mincost){
mincost=sum; // 更新最小费用
}
}
else{
for(int i=t;i<=n;i++){
swap(x[t], x[i]); // 交换工作分配人选
sum+=c[t][x[t]]; // 将工作t分配给人x[t],并更新费用
// 如果当前得到的sum小于最小值,继续向下搜索子树;否则剪枝
if(sum<mincost){
backtrack(t+1); // 递归下一层
}
sum-=c[t][x[t]]; // 回溯,撤销费用
swap(x[t], x[i]); // 恢复原状态
}
}
}
int main(){
ifstream inputFile("input.txt");
ofstream outputFile("output.txt");
inputFile>>n;
// 输入工作分配费用
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
inputFile>>c[i][j];
}
x[i]=i; // 初始化分配人选,将工作i分配给人i
}
inputFile.close();
// 开始回溯
backtrack(1);
// 输出最小费用
outputFile<<mincost<<endl;
outputFile.close();
return 0;
}
// 运行完成后打开output.txt文件即可看到结果