HNU算法设计与分析(信安)实验四

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文件即可看到结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值