SRM539-div1-1-div2-2-Over9000Rocks


题目大意:
     从若干个盒子中任意选择几个装石头,每个盒子容量都有上下限,一旦选择使用某个盒子,那么填装的石头数必须在该盒子的上下限容量之间。假设最终填装的石头总数为X,那么符合条件X>9000的X有多少个?
     数据规模:盒子总数[1,15], 盒子容量[1,10^6]

思路:
     由于盒子数的最大值只有15,所以可以遍历所有的盒子选择方案,总共2^15=32K种。在每一种选择方案中,累加所有盒子的容量下限和上限作为总的容量下限和上限(下限要与9001取最大值),总容量下限都上限之间的所有值都是X的可能值。汇总所有方案下的所有取值(需要去重)就可以得到最终结果。
     由于每种方案下的取值区间最小值都可以达到接近10^6,所以一个个记录可能取值肯定会超时的(2^15*10^6)。好在我们只需要记录每中方案下的取值范围起始值和终止值便可。最终根据这些记录的范围来计算所有可能取值数。有两种实现方式(假设盒子数为k):
  • 保存每种方案下的取值范围二元组[s,t],总共有2^k。对这些二元组进行从小到大排序,二元组中s为主键,t为第二主键。然后从小到大扫描这些二元组,过程中保留目前所遇到的X可行取值的最大值lastMax。对于当前二元组[s,t],分别比较s、t与lastMax的大小关系去除重复的取值,将新发现的取值范围的长度加到最终结果中,然后更新lastMax。
  • 利用一个大小为15×10^6+2的数组short[] bound保存各方案下的取值范围,假设某个方案的取值范围是[s,t],则bound[s]++, bound[t+1]--。然后遍历从9001到15×10^6的每个值x,考虑bound[9001]+bound[9002]+...+bound[x]的取值,如果大于0则说明x处于某个取值范围中,否则不是。(该数组的大小可以优化为所有盒子容量上限的加和)。
     第二种方法在最坏情况下空间和时间消耗都更大,但是编码更简单些,以下代码使用第二种实现方式。

Java代码:

public class Over9000Rocks {
    public int countPossibilities( int[] lowerBound, int[] upperBound) {
        int n = lowerBound.length ;
        int maxRes = 0;
        for (int i = 0; i < n; ++i){
            maxRes += upperBound[i];
        }
        int [] bound = new int[maxRes + 2];
        for (int m = 1; m < (1 << n); ++m) {
            int low = 0, up = 0;
            for (int i = 0; i < n; ++i) {
                if ((m & (1 << i)) > 0) {
                    low += lowerBound[i];
                    up += upperBound[i];
                }
            }
            low = Math. max(low, 9001);
            if (up < low) {
                continue ;
            }
            bound[low]++;
            bound[up + 1]--;
        }
        int res = 0;
        int v = 0;
        for (int x = 9001; x <= maxRes; ++x){
            if ((v += bound[x]) > 0){
                res++;
            }
        }
        return res;
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值