POJ 1018 Communication System [枚举]

大致题意:
某公司要建立一套通信系统,该通信系统需要n种设备,而每种设备分别可以有m1、m2、m3、...、mn个厂家
提供生产,而每个厂家生产的同种设备都会存在两个方面的差别:带宽bandwidths 和 价格prices。
现在每种设备都各需要1个,考虑到性价比问题,要求所挑选出来的n件设备,要使得B/P最大。
其中B为这n件设备的带宽的最小值,P为这n件设备的总价。


解题思路:

首先需要明确,要使得B/P最大,自然是要令B尽可能大,P尽可能小。

由于B和P是两个相互制约的变量,而他们又要同时取得尽可能地取极值,那么可以先令其中一个值“暂时固定”下来。

令输入的行数就是设备的种类数,即第i行描述的是第i种设备,及提供第i种设备的厂家的产品信息。

 

使用枚举+剪枝的做法:

首先B的值肯定是厂家生产的所有设备中的某个带宽值,所以可以枚举所有的带宽,每次枚举B值时,B值就被“暂时固定”了。

其次,记录所选取的B是属于第k种设备的,再从余下的设备中,选取其余n-1种设备各一个,要求所选取的设备的带宽>=B(这是由题意确定的),而价格是要满足带宽的要求下的最小值。

当n种设备都选取后,计算B/P,然后再枚举下一个B,重复上述操作。比较不同的B值所得到的B/P值,选取最大的一个就是答案。

 

剪枝法:

准备工作:

1、输入时先记录:对于每种设备,厂家所提供的最大带宽MaxB[]

2、对所有设备(无论是否同种类)进行升序快排,以带宽为第一关键字,价格为第二关键字,设备所属的种类编号(1~n)为第三关键字。排序后存放在一维数组dev[]

剪枝:

1、  从小到大枚举dev[]中各个设备的带宽作为B值,设总设备数位m,则从1枚举到m-(n-1)。这是因为至少需要选择从枚举位置开始后面的n种设备,m-(n-1)是上限值,即恰好最后n件设备分别为n种设备。

2、  枚举B值的过程中,若发现B值比某种设备的最大带宽更大,则无需继续枚举,输出前面得到的B/P值。这是因为B是所有设备的最小带宽,若出现某个设备的最大带宽比B小,则说明B的选择是不合法的,又dev[]已按B升序排序,后面的B值更大,也不可能成立,因此无需继续枚举。

3、  枚举B值过程中,对于每个B值,在选择其他设备时要记录选取不同种类的设备个数count。最后当count<n时,说明B值位置往后剩余的设备中已无法提供n-1种不同设备,可直接跳出枚举。

-----------------------------//上面分析转自:YoU http://blog.csdn.net/lyy289065406/article/details/6676781

我的代码:

P.S. 在HDU上用GNU C++ 一直是 WA ,但是在POJ上用 C++ 交就能过(

Accepted312K110MSC++
),真心搞不懂。。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 101
struct NODE
{
    int b;//宽带
    int p;//价格
    int id;//设备编号
};
//按带宽、价格、编号依次比较排序
int cmp(const void* a, const void* b)
{
    NODE* x =(NODE*)a;
    NODE* y =(NODE*)b;
    if((x->b)==(y->b))
    {
        if((x->p)==(y->p))
            return (x->id) -(y->id);
        else
            return (x->p) - (y->p);
    }
    return (x->b) -(y->b);
}

double max(double a, double b)
{
    return a > b? a:b;
}
int MaxB[N]; //各种设备对应的带宽最大值
NODE dev[N*N]; //记录所有厂家生产的产品信息
bool vist[N];
int pd; //dev[]指针

int main()
{
    int test, i, j;
    //freopen("in.txt","r",stdin);
    scanf("%d",&test);
    while(test--)
    {
        int n;//设备数
        int m = 0; //生产商总数
        pd = 0;
        scanf("%d",&n);
        for(i=1;i<=n;i++)
        {
            int mi;
            scanf("%d",&mi);
            m +=mi;
            MaxB[i] =-1;
            for(j=1;j<=mi;j++)
            {
                pd++;
                scanf("%d%d",&dev[pd].b,&dev[pd].p);
                dev[pd].id=i;
                MaxB[i] = max(MaxB[i], dev[pd].b);
            }
        }
        qsort(dev,m+1,sizeof(NODE),cmp);

        bool flag = false;
        double ans = 0; // B/P的最大值
        for(i=1;i<=m-(n-1);i++) //枚举所有设备带宽的最小带宽B
        { 			//m-(n-1)是剪枝,因为当设备数>生产商时就不必枚举了
            memset(vist,false,sizeof(bool)*(n+1));
            vist[dev[i].id] = true;
            double price = dev[i].p; //设备总价
            int count = 1;   //计数器,记录已经选取的设备个数
            for(j=i+1;j<=m;j++)
            {
                if(vist[ dev[j].id])
                continue;
                if(dev[i].b > MaxB[dev[j].id]) //剪枝
                {
                    flag= true;  //当前枚举的“所有设备带宽的最小带宽Bi”比“设备j的最大带宽MaxBj”要大
				//说明当前Bi已经越界,无需继续往后枚举
                    break;
                }
                vist[dev[j].id] =true;
                price +=dev[j].p;
                count++;
            }
            if(flag || count<n)
                break;
            ans = max(ans, (dev[i].b/price));
        }
        printf("%.3lf\n",ans);
    }
    return 0;
}









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值