算法作业(装载问题)

实验六 装载问题

问题描述与实验目的:

有n个集装箱要装上2艘载重量分别为c1和c2的轮船,其中第i个集装箱的重量为wi,要求确定是否有一个合理的装载方案可将这个集装箱装上这2艘轮船。如果有,找出一种装载方案。
注意,在满足的条件下才可能将这个集装箱装上这2艘轮船。

输入

输入有若干组测试数据(不超过20组)。
每组测试数据有3行:其第1行上是集装箱个数n,(n<20),第2行上有n个整数w1、w2、…、wn,整数之间用一个空格分开,这n个整数依次表示这n个集装箱的重量,接下来的一行上有2个整数c1、c2,表示这两艘船的载重量,(0<wi <1000,i=1,2,…,n,0<c1,c2<30000)。

输出

现要求对输入中的每组测试数据,输出2行:
在第1行上输出“Case #”,其中“#”是测试数据的组号(从1开始)。
在第2行上输出具体装载结果:如不能装载则输出“No”;否则输出一个整数bestw及一个由0或1构成的字符串x1x2…xn,其中bestw表示第一艘船能装的最大总载重量,x1x2…xn是对应于bestw的具体装载方案,xi=1表示第i个集装箱装在第一艘船上,而xi=0表示第i个集装箱不装在第一艘船上。
对应于bestw的具体装载方案可能不唯一,如重量分别为10、40、40的3个集装箱,若两船的载重量都是50,那么有装载方案110和101两种。为使输出结果可操作,我们约定长为n的0-1字符串以字典序最大的那个为符合要求的装载方案。
如可能,将计算时间复杂性限制在$ O(2^n)$

输入样例

3
10 40 40
50 50
3
20 40 40
50 50

输出

Case 1
50 110
Case 2
No

分析:

本次实验的重点在于将 O ( n ∗ 2 n ) O(n*2^n) O(n2n)的复杂度优化到$ O(2^n)$,我采用的方法是,在dfs过程中动态的更新路径best。具体实现是:在dfs时记录x[i]和best[i],在回溯的过程中,用x[i]来更新best[i]。
dfs结束以后,看记录的最多可装载量ans+c2与总量之间的关系,如果ans+c2<tot,那么之间输出No,反之,输出ans以及路径。

运行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZqbS876g-1584347058752)(./捕获.PNG)]

源代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn=100;
int n;
int a[maxn];
int c1,c2;
int x[maxn],ans,best[maxn];
int cw;
int ii,r;
void dfs(int i)
{
    if(i>n)
    {
        ii=n;
        ans=cw;
        return;
    }
    r-=a[i];
    if(cw+a[i]<=c1)
    {
        x[i]=1;
        cw+=a[i];
        dfs(i+1);
        if(ii==i) 
        {
            best[i]=1;
            ii--;
        }
        cw-=a[i];
    }
    if(cw+r>ans)
    {
        x[i]=0;
        dfs(i+1);
        if(ii==i)
        {
            best[i]=0;
            ii--;
        }
    }
    r+=a[i];
}
int main()
{
    #ifndef ONLINE_JUDGE
        freopen("in.txt","r",stdin);
        freopen("out.txt","w",stdout);
    #endif
    int Case=0;
    while(scanf("%d",&n)!=EOF)
    {
        if(n==0) break;
        int s=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            s+=a[i];
        }
        cw=0,ans=0;
        r=s;
        scanf("%d%d",&c1,&c2);
        dfs(1);
        printf("Case %d\n",++Case);
        if(ans+c2<s)
        {
            printf("No\n");
            continue;
        }
        printf("%d  ",ans);
        for(int i=1;i<=n;i++)
        {
            printf("%d",best[i]);
        }
        printf("\n");
    }
    return 0;
}

实验体会
通过本次实验,我对回溯的理解又加深了,也体会到了剪支对搜索算法非常有用。

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
装载问题是一个经典的计算机科学问题,通常用于优化操作系统中的内存管理。在C语言中,可以使用贪心算法来解决这个问题。具体步骤如下: 1. 将所有可用的内存块按照大小从大到小排序。 2. 逐一将作业按照大小从大到小排序。 3. 对于每个作业,从可用内存块中选择一个大小不小于该作业的内存块,将该作业装入内存,并将该内存块从可用内存块列表中删除。 4. 如果没有可用内存块满足当前作业,则放弃该作业。 下面是一个简单的C语言实现: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_JOBS 100 #define MAX_BLOCKS 100 typedef struct { int size; int index; } Block; typedef struct { int size; int index; } Job; int compare(const void *a, const void *b) { return ((Block*)b)->size - ((Block*)a)->size; } int main() { int num_jobs, num_blocks; Job jobs[MAX_JOBS]; Block blocks[MAX_BLOCKS]; int block_used[MAX_BLOCKS] = {0}; int i, j; // 读入作业和内存块信息 printf("请输入作业数:"); scanf("%d", &num_jobs); printf("请输入每个作业的大小:\n"); for (i = 0; i < num_jobs; i++) { scanf("%d", &jobs[i].size); jobs[i].index = i; } printf("请输入内存块数:"); scanf("%d", &num_blocks); printf("请输入每个内存块的大小:\n"); for (i = 0; i < num_blocks; i++) { scanf("%d", &blocks[i].size); blocks[i].index = i; } // 按照大小排序 qsort(blocks, num_blocks, sizeof(Block), compare); qsort(jobs, num_jobs, sizeof(Job), compare); // 装载作业 for (i = 0; i < num_jobs; i++) { for (j = 0; j < num_blocks; j++) { if (block_used[j]) continue; if (blocks[j].size >= jobs[i].size) { printf("作业 %d 被装入内存块 %d\n", jobs[i].index, blocks[j].index); block_used[j] = 1; break; } } if (j == num_blocks) { printf("作业 %d 无法被装入内存\n", jobs[i].index); } } return 0; } ``` 在这个例子中,我们首先读入了作业和内存块的信息,然后按照大小排序,接着逐一将作业装入内存块中。对于每个作业,我们从可用内存块中选择一个大小不小于该作业的内存块,将该作业装入内存,并将该内存块从可用内存块列表中删除。如果没有可用内存块满足当前作业,则放弃该作业。最后输出每个作业被装入的内存块。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值