Codejam之Ratatouille

问题描述

每一个包裹含有一定量的原料,不同的包裹即使含有相同的原料,量可能不一样。对于每一种原料,你有相同数量的包裹。

用这些包裹组成尽可能多的套装(kit)提供给顾客。一个kit中对每一种原料都含有1个包裹,一个kit可以做成多份ratatouille(这个kit的label,必须是整数)。为了不浪费,每个包裹所包含的原料的量必须在做成label份ratatouille实际用量的90%到110%之间。

比如,做成一份ratatouille需要500g西红柿和300g洋葱,现有一个900g的西红柿包裹,和一个660g的洋葱包裹,这两个包裹可以组成一个kit,做成2份ratatouille(包裹的label是2)。做成两份ratatouille需要西红柿1000g,洋葱600g,900g在1000g的[90%, 110%]之间,660g在600g的[90%, 110%]之间,因此可以组成kit。

目的是组成尽可能多的kit,每个包裹至多只能用在一个kit中,注意目的不是做成尽可能多份ratatouille

输入:
第一行T,代表有T个测试用例
每个测试用例如下:
一行两个数字,N(需要多少种原料),P(每个原料的包裹的数量)
一行N个数字Ri,Ri代表做成一份ratatouille需要多少g第i种原料
N行,每行P个数字,第i行的第j个数值Qij代表包含第i种原料的第j个包裹包含多少g这种原料。

输出:
最多能够组成多少个kit

限制:
1<=T<=100
1<=Ri<=106
1<=Qij<=106

小数据集:
1<=N<=2
1<=P<=8

大数据集:
1<=N<=50
1<=P<=50
N×P<=1000
这里写图片描述

问题解决

Q=包裹中含有该原料的量
R=做成一份ratatouille需要该原料的量
m × R × 90% <= Q <=110% × m × R
所以Q / ( 1.1R ) <= m <= Q / ( 0.9R )
包裹装了多少g原料是不重要的,重要的是包裹能够用于哪些label的kit,通过上面的不等式可以得到一个range,注意range可以为空(如R=10,Q=15)

public static class range{
    int s;
    int e;
    public range(int s,int e){
        this.s=s;
        this.e=e;
    }
}
//ingre_no 需要多少种原料
//pack_no 每种原料有多少个包裹
//ingre_needs[1...ingre_no] 做一份ratatouille需要原料i多少g
List[] packs = new LinkedList[ingre_no+1];
for(int i=1;i<=ingre_no;i++){
    aline=reader.readLine();
    List<range> lis=new LinkedList<range>();
    for(int j=1;j<=pack_no;j++){
        int pack_gram=Integer.parseInt(aline.split(" ")[j-1]);
        //计算这个包裹的range              
        int s=(10*pack_gram)/(ingre_needs[i]*11)+(((10*pack_gram)%(ingre_needs[i]*11)==0)?0:1);
        int e=(10*pack_gram)/(ingre_needs[i]*9);
        if(s<=e){    //如果range为空,无需进入列表
            lis.add(new range(s,e));
        }
    }
    range[] packi =new range[lis.size()];
    lis.toArray(packi);
    sort(packi);   //对range进行排序,s小的在前面,如果s相等比较e,e小的在前面
    lis.clear();
    for(int j=0;j<packi.length;j++){
        lis.add(packi[j]);
    }
    packs[i]=lis;
}

针对每一种原料,读取这种原料的包裹的含量并计算range,加入列表。对列表进行排序,s小的在前面,如果s相等再比较e,e小的在前面。

如果只需要一种原料,结果就是该原料对应的列表的大小,即有多少个range不为空的包裹就能组成多少个kit。

如果有N种原料,遍历N种原料,如果有一个原料对应的列表为空,则不可能组成一个kit。

取出N种原料对应的N个列表的第一个range
1. 这N个range的交集不为空,则可以组成一个kit,去掉这N个range。
2. 这N个range的交集为空,去掉上界最小的哪个range
重复这一步骤直至有列表为空。

int res=0;
if(ingre_no==1){
    res=packs[1].size();
}
else{
    boolean flag=true;
    for(int i=1;i<=ingre_no;i++){
        if(packs[i].size()==0){
            flag=false;
        }
    }
    while(flag){
        boolean flag2=true;
        int most_low_upper=1;
        range r1=(range)packs[1].get(0);
        for(int i=2;i<=ingre_no;i++){
            range r2=(range)packs[i].get(0);
            if(r2.e<r1.e) most_low_upper=i;
            if(r2.s>r1.e || r2.e<r1.s){
                flag2=false;
            }
        }
        if(flag2){
            res++;
            for(int i=1;i<=ingre_no;i++){
                packs[i].remove(0);
            }
        }else{
            packs[most_low_upper].remove(0);
        }
        for(int i=1;i<=ingre_no;i++){
            if(packs[i].size()==0){
                flag=false;
            }
        }
    }
}
writer.write("Case #"+k+": "+res+'\n');
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值