HNU_算法_实验4(2021级)-算法实验1-回溯法求解工作分配问题

本文介绍了如何使用回溯算法解决工作分配问题,通过实例演示了算法设计和实现过程,探讨了时间复杂度为O(n!)的情况,并提供了生成随机数据和测试结果的代码。实验者分享了实验心得,包括算法的理解和存在的问题。
摘要由CSDN通过智能技术生成

 一、实验名称

回溯算法实现题 5-13 工作分配问题:

        问题描述:

设有n件工作分配给n个人。将工作i分配给第j个人所需的费用为 c i j。试设计一个算法,为每一个人都分配1件不同的工作,并使总费用达到最小。

设计一个算法,对于给定的工作费用,计算最佳工作分配方案,使总费用达到最小。

二、实验目的

        通过上机实验,要求掌握回溯算法求解工作分配问题的问题描述、算法设计思想、程序设计。

三、实验原理

        解决回溯算法实现工作分配问题,并计算出程序运行所需要的时间。

四、实验步骤

        举例:

输入如下:

输入数据的第一行有1 个正整数n (1≤n≤20)。接下来的n行,每行n个数,表示工作费用。

3

10 2 3

2 3 4

3 4 5

结果为:9

        解题思路:

d(i,j)来描述A[1:i]和B[1:j]两字符串之间的扩展距离:

为工作 i 安排第 j 个人的解决方法与“运动员最佳匹配问题”类似,让一方选另一方,这样就可以构成一棵排列树。我们让工作“选”人,那排列树的结点代表人,而层就代表工作。如下图左上角的G1表示工人1,且在第一层,表示为工作 1 安排第 1 个人,即将工作1分配给工人1。

此外,最小值可能有多个,但效果一样,最终返回的都是9。

五、关键代码

1.回溯法求解

//全局变量
long long Min;   //因为要求最小值,所以将Min初始化为最大整数(int型)
 
// 回溯法求解
void dfs(int t,int n,long long &sum,vector<int> &book,vector<vector<int> > &pay)
{
   //已经到达叶子结点
    if(t>=n)   //继续判断是否找到了最小总费用
    {
        if(Min>sum)   //没有找到最小总费用
        {
//更新最小总费用
            Min=sum;   
            return;
        }
    }
    for(int i=0;i<n;i++)   //为第工作t安排人
{
   //第i个人还没有被安排工作
        if(!book[i])   
        {
            book[i]=1;   //将工作t分配给第i个人
           //更新总费用
            sum+=pay[t][i];   
            //如果当前得到的sum小于最小值,就向下搜索子树;否则剪枝
            if(sum<Min)                  
dfs(t+1,n,sum,book,pay);
            book[i]=0;   //没有得到比Min更小的和,回溯
            sum-=pay[t][i];
        }
    }
 
}

2.生成不同规模样例:

 //生成规模为n的随机数 
        cout<<"请输入数据规模n:"<<endl; 
        int n;        
        cin>>n;
        ofstream out("input1.txt");
        out<<n<<'\n';
        srand((unsigned)time(NULL));
        // pay (a,b]
        int a=0,b=100;
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                out<< (rand() % (b-a))+ a + 1<<" ";
            }
            out<<endl;
        }        
        out.close();

六、测试结果

时间复杂度: O(n!)

        对于工作分配问题,如果有 n 个工人和 n 个任务,一种简单的回溯算法的时间复杂度是 O(n!)。其解空间是一个排列组合问题。对于每个工人,都有 n 种可能的任务分配方案。因此,总的搜索空间是 n 的阶乘 (n!)。

        在回溯算法中,通常会使用剪枝技术来减少搜索空间,提高算法效率。但在最坏情况下,当没有剪枝的情况下,时间复杂度仍然是 O(n!)。这是因为在每一步,都要考虑所有可能的分支。

自定义输入n的数值的测试结果:

七、实验心得

        通过这次实验,我了解熟悉了回溯算法实现工作分配问题的求解过程及原理。对于自己实现的案例,感觉存在误差,画出来的图不符合直觉,也可能是求解运行时间的程序有问题。

八、完整代码

#include <iostream>
#include <fstream>
#include <windows.h>
#include <time.h>
#include <cmath>
#include <vector>
using namespace std;
 
// 定义一个宏,用于获取三个数的最小值
#define mins(a,b,c) min(min(a,b),c)
 
int dpComp(int k,string A,string B)
{
    int len1=A.length(),len2=B.length();
    vector<vector<int> >dp(len1+1,vector<int>(len2+1));
    for(int i=1;i<=len1;i++)//预处理
    {
        dp[i][0]=k*i;
    }
    for(int i=1;i<=len2;i++)//预处理
    {
        dp[0][i]=k*i;
    }
    dp[0][0]=0;
    
    for(int i=1;i<=len1;i++)
    {
        for(int j=1;j<=len2;j++)
        {
            dp[i][j]=mins(dp[i-1][j]+k,dp[i][j-1]+k,dp[i-1][j-1]+abs(A[i-1]-B[j-1]));
        }
    }
    return dp[len1][len2];
}
 
int main(int argc, char** argv) {
    ofstream out1("output.txt");
    int x=1;
    while(1){
        //生成规模为n的随机数 
        cout<<"请输入数据规模n:"<<endl; 
        int n,c;
        cin>>n;
        ofstream out("input1.txt");
        srand((unsigned)time(NULL));
        /*
        ASCII码的可见字符包括数字、字母、标点符号和一些特殊字符。
        其中,0-31是控制字符,32是空格,33-126是可打印字符,127是DEL(删除)字符。
        */
        //字符串A长度为n,都为可打印字符 
        for(int i=0;i<n;i++){
            out<<static_cast<char>(' ' + std::rand() % (126 - ' ' + 1));
        }
        out<<endl;
        
        //[a,b] 
        int a=0,b=n;
        int Bl=(rand() % (b-a))+ a + 1;
        //字符串B长度为Bl,范围是[a,b],都为可打印字符
        for(int i=0;i<Bl;i++){
            out<<static_cast<char>(' ' + std::rand() % (126 - ' ' + 1));
        }
        out<<endl;
        //k,不妨设k的值在0-100之间
        a=0,b=100;
        out<<(rand() % (b-a))+ a + 1<<"\n";
        out.close();
    
        int i,maxi,mini;
        LARGE_INTEGER nFreq,nBegin,nEnd;
        double time; 
        
        ifstream in("input1.txt");
        string A,B;
        //in>>A;
        getline(in,A);
        n=A.length();
        
        //in>>B;
        getline(in,B);
        Bl=B.length();
    
        if(n<1000){
            cout<<"字符串A: "<<n<<"\t"<<A<<endl;
            cout<<"字符串B: "<<Bl<<"\t"<<B<<endl;
        }
        else{
            cout<<"字符串A: "<<n<<endl;
            cout<<"字符串B: "<<Bl<<endl;            
        }
        int k;
        in>>k;
        cout<<"空格与其它字符的距离k: "<<k<<endl;
        long long result;
            QueryPerformanceFrequency(&nFreq);    
            QueryPerformanceCounter(&nBegin);
            result=dpComp(k,A,B);
            QueryPerformanceCounter(&nEnd);
            time=(double)(nEnd.QuadPart-nBegin.QuadPart)/(double)nFreq.QuadPart; 
        cout<<"结果:"<<result<<"\n查询时间:"<<time<<endl<<endl;
        out1<<n<<' '<<time<<endl;
        in.close();    
    }
    out1.close();
    return 0;
}

九、绘图代码

import matplotlib.pyplot as plt

# 读取txt文件,假设文件名为data.txt
file_path = 'F:\\3-CourseMaterials\\3-1\\3-算法设计与分析\实验\lab3\\1-code\\3-字符串比较问题\\output.txt'

# 存储x和y的列表
x_values = []
y_values = []

# 读取文件并提取数据
with open(file_path, 'r') as file:
    for line in file:
        # 假设数据以空格或逗号分隔
        x, y = map(float, line.strip().split())
        x_values.append(x)
        y_values.append(y)

print(x_values)
print(y_values)
# 绘制图形
plt.plot(x_values, y_values)
plt.title('X vs Y Plot')
plt.xlabel('X-axis')
plt.ylabel('Y-axis')
plt.grid(True)
plt.show()


                      

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值