poj1018

大致题意:

某公司要建立一套通信系统,该通信系统需要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种不同设备,可直接跳出枚举。

 


剪枝2比较难懂,再稍微解释一下,以给的数据为例: 
1 3 
3 100 25 150 35 80 25 
2 120 80 155 40 
2 100 100 120 110

把该组数据升序排序,得到:

B:  80  100  100  120  120  150  155

P:  25  25   100   80  110   35   40

Id:  1   1     3    2    3    1    2


这样当B枚举到150的时候,即B=150,第三个供应商的所有设备都小于150,取任何一个设备都会导致B<150,矛盾。当然大于150的更不用枚举了,直接剪掉。 

#include<iostream>  
#include<algorithm>  
#include<iomanip>  
using namespace std;  
  
class info  
{  
public:  
    int B;   //带宽  
    double P;   //价格  
    int id;  //设备号码  
};  
  
int cmp(const void* a,const void* b)  
{  
    info* x=(info*)a;  
    info* y=(info*)b;  
  
    if((x->B)==(y->B))   //当带宽相等时  
    {  
        if((x->P)==(y->P))   //当价格也相等时  
            return (x->id)-(y->id);   //以编号为第三优先升序排序  
  
        return (x->P)-(y->P);   //以价格为第二优先升序排序  
    }  
  
    return (x->B)-(y->B);   //以带宽为第一优先升序排序  
}  
  
double max(double a,double b)  
{  
    return a>b?a:b;  
}  
  
int main(int i,int j)  
{  
    int test;  
    cin>>test;  
    for(int t=1;t<=test;t++)  
    {  
        int n;  //设备数  
        int m=0;  //生产商总数  
        cin>>n;  
  
        int* MaxB=new int[n+1];  //各种设备对应的带宽最大值  
        info* dev=new info[100*100+1];     //记录所有厂家生产的产品信息  
  
        int pd=0;  //dev[]指针  
  
        /*Input*/  
  
          
        for(i=1;i<=n;i++)  
        {  
            int mi;  
            cin>>mi;  
            m+=mi;  
  
            MaxB[i]=-1;  
            for(j=1;j<=mi;j++)  
            {  
                pd++;  
                cin>>dev[pd].B>>dev[pd].P;  
                dev[pd].id=i;  
  
                MaxB[i]=max(MaxB[i],dev[pd].B);  
            }  
        }  
  
        /*Qsort*/  
  
        qsort(dev,m+1,sizeof(info),cmp);  
  
        /*Enum*/  
  
        bool flag=false;  
        double ans=0;  // B/P的最大值  
        for(i=1;i<=m-(n-1);i++)  //枚举所有设备带宽的最小带宽B  
        {                        //m-(n-1)是剪枝,因为当设备数>生产商数时就不必枚举了  
            bool* vist=new bool[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" 要大  
                    break;      //说明当前Bi已经越界,无需继续往后枚举  
                }  
  
                vist[ dev[j].id ]=true;  
                price+=dev[j].P;  
                count++;  
            }  
            if(flag || count<n)  
                break;  
  
            ans=max(ans,(dev[i].B/price));  
        }  
  
        cout<<fixed<<setprecision(3)<<ans<<endl;  
  
        delete MaxB;  
        delete dev;  
    }  
    return 0;  
}  


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
POJ 2182是一道使用树状数组解决的题目,题目要求对给定的n个数进行排序,并且输出每个数在排序后的相对位置。树状数组是一种用来高效处理前缀和问题的数据结构。 根据引用中的描述,我们可以通过遍历数组a,对于每个元素a[i],可以使用二分查找找到a到a[i-1]中小于a[i]的数的个数。这个个数就是它在排序后的相对位置。 代码中的query函数用来求前缀和,add函数用来更新树状数组。在主函数中,我们从后往前遍历数组a,通过二分查找找到每个元素在排序后的相对位置,并将结果存入ans数组中。 最后,我们按顺序输出ans数组的元素即可得到排序后的相对位置。 参考代码如下: ```C++ #include <iostream> #include <cstdio> using namespace std; int n, a += y; } } int main() { scanf("%d", &n); f = 1; for (int i = 2; i <= n; i++) { scanf("%d", &a[i]); f[i = i & -i; } for (int i = n; i >= 1; i--) { int l = 1, r = n; while (l <= r) { int mid = (l + r) / 2; int k = query(mid - 1); if (a[i > k) { l = mid + 1; } else if (a[i < k) { r = mid - 1; } else { while (b[mid]) { mid++; } ans[i = mid; b[mid = true; add(mid, -1); break; } } } for (int i = 1; i <= n; i++) { printf("%d\n", ans[i]); } return 0; } ``` 这段代码使用了树状数组来完成题目要求的排序功能,其中query函数用来求前缀和,add函数用来更新树状数组。在主函数中,我们从后往前遍历数组a,通过二分查找找到每个元素在排序后的相对位置,并将结果存入ans数组中。最后,我们按顺序输出ans数组的元素即可得到排序后的相对位置。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [poj2182Lost Cows——树状数组快速查找](https://blog.csdn.net/aodan5477/article/details/102045839)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [poj_2182 线段树/树状数组](https://blog.csdn.net/weixin_34138139/article/details/86389799)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值