算法:确定最小值/最大值的组合是否落在给定范围内

时间:2013-08-21 23:41:48

标签: algorithm

想象一下,你有3个水桶,但每个水桶都有一个洞。我正试着填满浴缸。浴缸所需的水量最低,可容纳的水量最多。当你带着水桶到达浴缸时,不清楚桶里有多少水,但你有一系列可能的值。

是否可以用水充分填充浴缸?

你差不多有3个范围(最小值,最大值),它们的总和是否会落在第4范围内?

例如: 铲斗1:5-10L 铲斗2:15-25L 铲斗3:10-50L

浴缸100-150L

是否有一些保证组合1 2和3可以在必要范围内填充浴缸?可以使用每个桶的倍数。

编辑:现在想象有50个不同的桶?

7 个答案:

答案 0 :(得分:1)

如果浴缸的容量不是很大(例如,不超过10 ^ 6),我们可以使用动态编程解决它。

<强>方法

初始化:备忘录[X] [Y]是一个记忆结果的数组。 X =桶的数量,Y =桶的最大容量。使用-1初始化memo [] []。

<强>代码:

bool dp(int bucketNum, int curVolume){

    if(curVolume > maxCap)return false;             // pruning extra branches

    if(curVolume>=minCap && curVolume<=maxCap){     // base case on success
        return true;
    }

    int &ret = memo[bucketNum][curVolume];
    if(ret != -1){                                  // this state has been visited earlier
        return false;
    }
    ret = false;

    for(int i = minC[bucketNum]; i < = maxC[bucketNum]; i++){
        int newVolume = curVolume + i;
        for(int j = bucketNum; j <= 3; j++){
            ret|=dp(j,newVolume);
            if(ret == true)return ret;
        }
    }
    return ret;
}

警告:代码未经过测试

答案 1 :(得分:1)

这是python中一个天真的递归解决方案,可以很好地工作(尽管它没有找到最佳解决方案):

def match_helper(lower, upper, units, least_difference, fail = dict()):
  if upper < lower + least_difference:
    return None
  if fail.get((lower,upper)):
    return None
  exact_match = [ u for u in units if u['lower'] >= lower and u['upper'] <= upper ]
  if exact_match:
    return [ exact_match[0] ]

  for unit in units:
    if unit['upper'] > upper:
      continue
    recursive_match = match_helper(lower - unit['lower'], upper - unit['upper'], units, least_difference)
    if recursive_match:
      return [unit] + recursive_match
  else:
    fail[(lower,upper)] = 1
    return None

def match(lower, upper):
  units = [
      { 'name': 'Bucket 1', 'lower': 5,  'upper': 10 },
      { 'name': 'Bucket 2', 'lower': 15, 'upper': 25 },
      { 'name': 'Bucket 3', 'lower': 10, 'upper': 50 }
  ]

  least_difference = min([ u['upper'] - u['lower'] for u in units ])
  return match_helper(
    lower = lower,
    upper = upper,
    units = sorted(units, key = lambda u: u['upper']),
    least_difference = min([ u['upper'] - u['lower'] for u in units ]),
  )

result = match(100, 175)
if result:
  lower = sum([ u['lower'] for u in result ])
  upper = sum([ u['upper'] for u in result ])
  names = [ u['name'] for u in result ]
  print lower, "-", upper
  print names
else:
  print "No solution"

它为100-150打印“无解”,但对于100-175,它提供了5x桶1,5x桶2的解决方案。

答案 2 :(得分:0)

假设您说每个水桶的“范围”是它到达浴缸时可能产生的水量,您关心的是它们是否可能填满浴缸......

只需取每个桶的“最大值”并求它们。如果这是你认为浴缸被“填充”的范围,那么就可以。

答案 3 :(得分:0)

更新:

鉴于桶可以多次使用,我觉得我们正在寻找一对方程的解决方案。

给定桶x,y和z我们想要找到a,b和c:

a*x.min + b*y.min + c*z.min >= bathtub.min

a*x.max + b*y.max + c*z.max <= bathtub.max

回复:http://en.wikipedia.org/wiki/Diophantine_equation

如果bath.min和bathtub.max都是a,b和c的最大公约数的倍数,那么有无限多的解(即我们可以填充浴缸),否则没有解决方案(即我们可以从不填满浴缸。)

答案 4 :(得分:0)

最初,您的目标范围是Bathtub.Range。

每次向解决方案添加存储桶实例时,都会减少剩余存储桶的目标范围。

例如,使用示例桶和桶:

目标范围= 100..150

假设我们想在候选解决方案中添加Bucket1。那就给了我们

目标范围= 95..140

因为如果解决方案中的其余桶总数 95,然后这个Bucket1可能不足以将浴缸填充到100,并且如果解决方案中剩余的桶总数> 100。 140,那么这个Bucket1可能会超过150桶。

因此,这可以让您快速检查候选解决方案是否有效:

TargetRange = Bathtub.Range
foreach Bucket in CandidateSolution
  TargetRange.Min -= Bucket.Min
  TargetRange.Max -= Bucket.Max
  if TargetRange.Min == 0 AND TargetRange.Max >= 0 then solution found
  if TargetRange.Min < 0 or TargetRange.Max < 0 then solution is invalid

这仍然存在一个问题 - 你如何提出候选解决方案?

蛮力会尝试所有可能的桶组合。

答案 5 :(得分:0)

这可以通过change making problem的多个应用来解决。

每个Bucket.Min值是货币面额,Bathtub.Min是目标值。

当您通过更改算法找到解决方案时,再应用一个约束:

总和(解决方案中的每个Bucket.Max)&lt; = Bathtub.max

如果不满足此约束,请抛弃此解决方案并查找另一个。这可能需要更改标准的更改算法,以便在发现其他解决方案不合适时尝试其他解决方案。

答案 6 :(得分:0)

这是我找到最佳解决方案(最少数量的桶)的解决方案。它将最大值与最小值之比进行比较,以确定填充桶的最佳铲斗数量。

    private static void BucketProblem()
    {
        Range bathTub = new Range(100, 175);

        List<Range> buckets = new List<Range> {new Range(5, 10), new Range(15, 25), new Range(10, 50)};

        Dictionary<Range, int> result;
        bool canBeFilled = SolveBuckets(bathTub, buckets, out result);
    }

    private static bool BucketHelper(Range tub, List<Range> buckets, Dictionary<Range, int> results)
    {
        Range bucket;
        int startBucket = -1;
        int fills = -1;
        for (int i = buckets.Count - 1; i >=0 ; i--)
        {
            bucket = buckets[i];
            double maxRatio = (double)tub.Maximum / bucket.Maximum;
            double minRatio = (double)tub.Minimum / bucket.Minimum;
            if (maxRatio >= minRatio)
            {
                startBucket = i;
                if (maxRatio - minRatio > 1)
                    fills = (int) minRatio + 1;
                else
                    fills = (int) maxRatio;
                break;
            }
        }          

        if (startBucket < 0)
            return false;
        bucket = buckets[startBucket];

        tub.Maximum -= bucket.Maximum * fills;
        tub.Minimum -= bucket.Minimum * fills;
        results.Add(bucket, fills);

        return tub.Maximum == 0 || tub.Minimum <= 0 || startBucket == 0 || BucketHelper(tub, buckets.GetRange(0, startBucket), results);
    }

    public static bool SolveBuckets(Range tub, List<Range> buckets, out Dictionary<Range, int> results)
    {
        results = new Dictionary<Range, int>();
        buckets = buckets.OrderBy(b => b.Minimum).ToList();
        return BucketHelper(new Range(tub.Minimum, tub.Maximum), buckets, results); 
    }