POJ1018 - Communication System 解题报告

POJ1018 - Communication System 解题报告

原题解析

题目说提供 t [1, 10] 个 case 。
每个 case 包括 n [1, 100] 个设备。
每个设备包括 i [1, 100] 个备选制造商提供的不同设备。
每个设备包括两个属性,带宽 b ,价格 p 。
题目要求每一个设备都要选择使用一个对应制造商提供的设备使得整个网络的带宽性价比最高。
其中,整体网络的带宽等于所有设备中的最小带宽,整体网络的价格等于所有设备价格的总和。

题目要点

  1. 带宽和价格没有给定范围,我只能先假设所有设备的带宽或价格的总和都不会超过 int 范围;
  2. 这里可以使用贪心算法或者 dp 来求解;

贪心

贪心的思路是,在确定最小带宽的情况下,系统的最高性价比等价于系统的最少价格。
因此可以从小到大枚举所有设备制造商提供的带宽,将其作为系统的最小带宽,并算出这个带宽下的最低价格。再得出一个带宽的最高性价比。
计算复杂度为 O(m * n2), m 为带宽区间,n为设备数量或供应商数量(这两者规模一样)。
这里带宽区间取决于有多少种不同价格的设备,最大为 n2

dp

状态定义为,指定带宽 b 时,从第 1 层到第 x 层系统的最小价格:

f[x][b]

可以预见:

f[0][b] = 0

既没有设备参与系统,价格为 0 。

状态的转移就是带宽 b 下 x 层系统的最小价格等于带宽 b 下 x-1 层系统的价格加上当前层的可用最小价格:

f[x][b] = f[x-1][b]+min(p[x][0], p[x][1], …, p[x][y])

这样最终可以算出带宽 b 下最大层系统的最小价格,按 b 进行枚举,找出性价比最高的即可。

计算复杂度同样为 O(m*n2),m 为带宽区间,n 为设备数量或供应商数量。

空间复杂度上,这里使用了一个技巧。因为实际上我们并不关心状态转移的过程,我们只关心最后一层的结果,并且带宽 b 的状态只能由带宽 b 产生,因此这里的层数 x 作为中间状态其实并不需要保留。
因此实际的空间变为:

f[x][b] => f[b]

而带宽空间的范围虽然不定,但是数量是有上限的,既 n2 个不同的带宽。因此这里将带宽离散化,排序后用序号进行状态的转移。因此这里的 b 的范围既 [1, 10000] 。

f[10000]

示例代码

贪心

#include <stdio.h>
#include <string.h>
#include <vector>
#include <algorithm>

struct State
{
    int n;
    int a[100];
    int b[100][100];
    int p[100][100];

    std::vector<int> bandwidths;

    double output;
};

void input(State&);
void calc(State&);
void output(State&);

bool findDevices(int& sumP, int minB, State& state);

int main()
{
    int caseNumber;
    scanf("%d", &caseNumber);
    while (caseNumber--)
    {
        State state;
        input(state);
        calc(state);
        output(state);
    }
    return 0;
}

void input(State& state)
{
    state.bandwidths.clear();
    state.output = 0;

    scanf("%d", &(state.n));
    for (int i = 0; i < state.n; ++i)
    {
        scanf("%d", &(state.a[i]));
        for (int j = 0; j < state.a[i]; ++j)
        {
            scanf("%d %d", &(state.b[i][j]), &(state.p[i][j]));
            state.bandwidths.push_back(state.b[i][j]);
        }
    }

    std::sort(state.bandwidths.begin(), state.bandwidths.end());
}

void calc(State& state)
{
    if (state.n > 0)
    {
        int lastB = 0;
        for (int i = 0; i < state.bandwidths.size(); ++i)
        {
            int minB = state.bandwidths[i];
            if (lastB == minB)
            {
                continue;
            }
            lastB = minB;

            int sumP = 0;

            bool found = findDevices(sumP, minB, state);

            if (!found)
            {
                continue;
            }

            state.output = std::max(state.output, (double)minB / sumP);
        }
    }
    else
    {
        state.output = 0;
    }
}

void output(State& state)
{
    printf("%0.3f\n", state.output);
}

bool findDevices(int& sumP, int minB, State& state)
{
    for (int x = 0; x < state.n; ++x)
    {
        bool foundRow = false;
        int minP = 0;
        double maxBpp = 0;
        for (int y = 0; y < state.a[x]; ++y)
        {
            if (state.b[x][y] >= minB)
            {
                double bpp = (double)minB / state.p[x][y];
                if (bpp > maxBpp)
                {
                    maxBpp = bpp;
                    minP = state.p[x][y];
                    foundRow = true;
                }
            }
        }
        if (!foundRow) return false;
        sumP += minP;
    }
    return true;
}

dp

#include <stdio.h>
#include <algorithm>
#include <vector>
#include <map>

struct State
{
    int n;
    int a[100];
    int b[100][100];
    int p[100][100];

    int f[1000];
    std::vector<int> bandwidths;

    double output;
};

void input(State&);
void calc(State&);
void output(const State&);

int main()
{
    int caseNumber;
    scanf("%d", &caseNumber);
    while (caseNumber--)
    {
        State state;
        input(state);
        calc(state);
        output(state);
    }
    return 0;
}

void input(State& state)
{
    scanf("%d", &(state.n));
    std::vector<int> bs;
    for (int i = 0; i < state.n; ++i)
    {
        scanf("%d", &(state.a[i]));
        for (int j = 0; j < state.a[i]; ++j)
        {
            scanf("%d %d", &(state.b[i][j]), &(state.p[i][j]));
            bs.push_back(state.b[i][j]);
        }
    }

    sort(bs.begin(), bs.end());

    int lastB = 0;
    for (int i = 0; i < bs.size(); ++i)
    {
        if (lastB != bs[i])
        {
            state.bandwidths.push_back(bs[i]);
            lastB = bs[i];
        }
    }
}

void calc(State& state)
{
    for (int i = 0; i < state.bandwidths.size(); ++i)
    {
        int bandwidth = state.bandwidths[i];
        double maxBpp = 0;
        int minP = 0;
        for (int y = 0; y < state.a[0]; ++y)
        {
            if (bandwidth > state.b[0][y])
            {
                continue;
            }
            double bpp =  (double)bandwidth / state.p[0][y];
            if (bpp > maxBpp)
            {
                maxBpp = bpp;
                minP = state.p[0][y];
            }
        }
        state.f[i] = minP;
    }

    for (int i = 0; i < state.bandwidths.size(); ++i)
    {
        for (int x = 1; x < state.n; ++x)
        {
            int bandwidth = state.bandwidths[i];
            double maxBpp = 0;
            int minP = 0;

            for (int y = 0; y < state.a[x]; ++y)
            {
                if (bandwidth > state.b[x][y])
                {
                    continue;
                }
                double bpp = (double)bandwidth / state.p[x][y];
                if (bpp > maxBpp)
                {
                    maxBpp = bpp;
                    minP = state.p[x][y];
                }
            }
            if (state.f[i] != 0 && minP != 0)
            {
                state.f[i] = state.f[i] + minP;
            }
            else
            {
                state.f[i] = 0;
            }
        }
    }

    state.output = 0;
    for (int i = 0; i < state.bandwidths.size(); ++i)
    {
        if (state.f[i] != 0)
        {
            state.output = std::max(state.output, (double)state.bandwidths[i] / state.f[i]);
        }
    }
}

void output(const State& state)
{
    printf("%0.3f\n", state.output);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值