机器分配 P2066 洛谷

#dp #背包求方案 #分组背包 #字典序 #T3

机器分配

P2066 机器分配 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目描述

总公司拥有高效设备M台,准备分给下属的N个分公司。各分公司若获得这些设备,可以为国家提供一定的盈利。问:如何分配这M台设备才能使国家得到的盈利最大?求出最大盈利值。其中M≤15,N≤10。分配原则:每个公司有权获得任意数目的设备,但总台数不超过设备数M。

输入格式

第一行有两个数,第一个数是分公司数N,第二个数是设备台数M。

接下来是一个N*M的矩阵,表明了第 I个公司分配 J台机器的盈利。

输出格式

第1行为最大盈利值

第2到第n为第i分公司分x台

P.S.要求答案的字典序最小

样例 #1

样例输入 #1

3 3
30 40 50
20 30 50
20 25 30

样例输出 #1

70
1 1
2 1
3 1

思路

揉合了 背包问题求具体方案 和 分组背包。

题目要求按字典序排列, 那我们输出答案时就不能从 n-1 方向来求, 需要用 1-n 来求, 根据反方向原则, 在求DP时要用n-1方向。

在看了紫书的教程后, 这里所做的逆向求解, 其实就是DAG那一节中的以i为起点的写法。

对于每个公司可以看做一组物品, 分组背包是枚举所有能选的, 这里一样, 求分组背包dp时要逆序n-1

然后再正序输出, 第一次循环公司, 第二层循环选几个, 如果满足:
f[i][j] == f[i + 1][j - k] + w[i][k]
则说明当前公司给了k台设备, break第二层循环后输出即可。

代码

const int N = 1010, M = 1010;
int v[N][M];
int f[N][M];

int n, m;

int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            cin >> v[i][j];
    memset(f, 0, sizeof f);
    for (int i = n; i >= 1; i--)
    {
        for (int j = 0; j <= m; j++)
        {
            for (int k = 0; k <= j; k++)
                f[i][j] = max(f[i][j], f[i + 1][j - k] + v[i][k]);
        }
    }
    cout << f[1][m] << endl;
    for (int i = 1, j = m; i <= n; i++)
    {
        int res = 0;
        for (int k = 0; k <= j; k++)
            if (f[i][j] == f[i + 1][j - k] + v[i][k])
            {
                res = k;
                j -= k;
                break;
            }
        cout << i << " " << res << endl;
    }
    return 0;
}
posted @ 2023-05-11 21:07  EdwinAze  阅读(56)  评论(0编辑  收藏  举报