uva 10163 Storage Keepers

原题:
Randy Company has N (1 ≤ N ≤ 100) storages. Company wants some men to keep them safe. Now
there are M (1 ≤ M ≤ 30) men asking for the job. Company will choose several from them. Randy
Company employs men following these rules:
1. Each keeper has a number P i (1 ≤ P i ≤ 1000) , which stands for their ability.
2. All storages are the same as each other.
3. A storage can only be lookd after by one keeper. But a keeper can look after several storages. If a
keeper’s ability number is P i , and he looks after K storages, each storage that he looks after has
a safe number U j = P i ÷ K.(Note: U j , P i and K are all integers). The storage which is looked
after by nobody will get a number 0.
4. If all the storages is at least given to a man, company will get a safe line L = minU j
5. Every month Randy Company will give each employed keeper a wage according to his ability
number. That means, if a keeper’s ability number is P i , he will get P i dollars every month. The
total money company will pay the keepers every month is Y dollars.
Now Randy Company gives you a list that contains all information about N, M, P, your task is give
company a best choice of the keepers to make the company pay the least money under the condition
that the safe line L is the highest.
Input
The input file contains several scenarios. Each of them consists of 2 lines:
The first line consists of two numbers (N and M), the second line consists of M numbers, meaning
P i (i = 1..M). There is only one space between two border numbers.
The input file is ended with N = 0 and M = 0.
Output
For each scenario, print a line containing two numbers L(max) and Y (min). There should be a space
between them.
Sample Input
2 1
7
1 2
10 9
2 5
10 8 6 4 1
5 4
1 1 1 1
0 0
Sample Output
3 7
10 10
8 18
0 0

中文:
给你一个仓库,仓库里有n个一样的货物,给你m个个守护货物的人,每个人的看守能力是 pi ,给定规则如下,
每个货物只能由一个人看守,一个人可以看守多个货物;每个被看守的货物都有一个安全值,这个值是由看守人员的能力决定的,如果一个看守人员 pi 看守k个货物,那么这k个货物的安全值系数就是 pi/k

现在让你求第一个值为这个仓库的安全值最大可以是多少,仓库的安全系数是所有货物安全系数中最小的那个。

在满足这个仓库安全系数最大的条件下,现在问你如何招聘人员才能使花费最少,花费就是每个看守人员的能力值。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int inf = 9999999;
int dp[31][101];
int pay[31][101];
int p[31], pp[31];
int n, m;

void ini()
{
    memset(dp, 0, sizeof(dp));
    for (int i = 0; i <= 30; i++)
    {
        dp[i][0] = -1;
        pay[i][0] = 0;
    }
    for (int i = 0; i <= 100; i++)
    {
        dp[0][i] = -1;
        pay[0][i] = inf;
    }
    pay[0][0] = 0;
    dp[0][0] = inf;
}

int main()
{
    ios::sync_with_stdio(false);
    while (cin >> n >> m, m + n)
    {
        for (int i = 1; i <= m; i++)
            cin >> p[i];
        ini();
        for (int i = 1; i <= m; i++)
        {
            dp[1][i] = p[1] / i;
        }
        for (int i = 1; i <= m; i++)
        {
            for (int j = 1; j <= n; j++)
            {
                int tmp,tmp2 = -1;
                for (int k = 1; k <= j; k++)
                {
                    if (i == 1 && j - k > 0)
                        continue;
                    if (dp[i - 1][j - k] >= 0)
                        tmp = min(dp[i - 1][j - k], p[i] / k);
                    else
                        tmp = p[i] / k;

                    tmp2 = max(tmp, tmp2);
                }
                dp[i][j] = max(tmp2, dp[i - 1][j]);
            }
        }
        int ans = dp[m][n];
        cout << ans <<" ";
        if (ans == 0)
        {
            cout << 0 << endl;
            continue;
        }
        int ind = 0;
        for (int i = 1; i <= m; i++)
            if (p[i] >= ans)
                pp[++ind] = p[i];
        for (int i = 1; i <= ind; i++)//ind个候选人
        {
            for (int j = 1; j <= n; j++)//填入n个
            {
                int tmp = inf;
                for (int k = 1; pp[i] / k>= ans && k <= j; k++)
                {
                    if (i == 1 && j - k > 0)
                        continue;
                    if (pay[i-1][j - k] >= 0)
                        tmp = Min(pay[i - 1][j - k] + pp[i], tmp);
                    else
                        tmp = pp[i];
                }
                pay[i][j] = Min(pay[i - 1][j], tmp);
            }
        }
        cout << pay[ind][n] << endl;
    }
    return 0;
}

解答:

首先考虑如何计算仓库的安全系数最大,类似0-1背包问题,对每个看守人可以选择“用”或者“不用”
,如果使用第i个人,那么让他看守几个货物? 在这里由于每个货物都是一样的,所以不用考虑货物的顺序。
状态转移方程可以得到为
dp[i][j]=max(min(dp[i1][jk],p[i]/k),dp[i1][j])
dp[i][j] 表示前i个人看守j个货物时,仓库的安全系数最大是多少。
min(dp[i1][jk],p[i]/k) 表示第i个人看守k个货物时,找到此时每个货物的安全系数最小值
dp[i1][j] 表示不用第i个人

得到仓库的最大安全系数以后,还要计算最小工资花费。

如果使用在计算仓库最大安全系数过程中寻找路径所得到的员工列表来计算工资和,结果是错误的。

仓库安全系数最大≠花费最低

因为在计算员工能力的时候,做除法是取整运算,能力值为7的员工看守3个货物和能力值为6的员工看守3个货物,安全系数相同,都是2。但是,花销一个是7,另一个是6。

由题目要求要找到在仓库安全系数最大的前提下,得到的工资最少。设得到的最优安全系数是ans,那么,录用的员工能力值一定是大于或者等于ans的。

把这些看守人员保存在数组pp当中,建立状态转移方程,同样是利用0-1背包的思想
pay[i][j]=min(pay[i1][jk]+pp[i],pay[i1][j])

pay[i][j] 表示取用前i个员工看守j个货物时的最小花费
pay[i1][jk]+pp[i] 表示取用第i个员工,看守k个货物时的最小花费
pay[i1][j] 表示不用第i个员工时的最小花费

其中注意,如果上面的最大安全系数得到值是0,那么一个员工都不用,最优花费也是0

同时,注意数据初始化的问题和边界的问题
pay[i][0] 表示有i个员工看守0货物时,此时花费应该是一个人都不用,值应为0

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值