The post office#2509

The post office (Editorial)

Let's introduce the notion of a mask that represents a subset of items to be shipped. For example, the mask mask = 5 = 101_2 specifies the subset containing the -th and -nd items (items are numbered from zero).

For each subset, we compute its total weight, which determines the shipping cost. If this weight is exactly grams, then since p \le 1000, it is always beneficial to use the special offer.

Let MinCost[mask] denote the minimum cost (in euros) of shipping the subset of items represented by . Then for any mask that is a subset of , the following relation holds:

MinCost[mask] = \min_{x \sub mask} (MinCost[mask~xor~x] + MinCost[x])

Since the mask 2^n - 1 corresponds to the entire set of items, the answer to the problem will be the value MinCost[2^n - 1].

Theorem. Enumerating all masks and all of their submasks takes O(3^n) time.

Proof 1. Consider the -th bit. There are three possible cases for it:

There are bits in total, so the total number of different combinations is 3^n.

Proof 2. Let a mask contain set bits. Then it has exactly 2^k submasks. The number of masks of length with exactly set bits is C_n^k. Therefore, the total number of combinations is

\sum_{k=0}^n C_n^k 2^k = (1 + 2)^n = 3^n

Algorithm implementation

We'll store the item weights in the array . In the cell MinCost[mask], we'll store the minimum shipping cost for the subset of items represented by the mask .

int m[15], MinCost[1 << 14];

Read the input data.

scanf("%d %d",&n,&p);
for (i = 0; i < n; i++) scanf("%d",&m[i]);

In the variable , iterate over all possible masks from to 2^n - 1.

for (mask = 0; mask < (1 << n); mask++)
{

Store in the variable the total weight of the subset of items corresponding to the mask . To do this, examine the binary representation of : if the bit at position is , then add the weight of the -th item to .

  for (Cost = i = 0; i < n; i++)
    if (mask & (1 << i)) Cost += m[i];

If the value of is strictly equal to , then since the problem states that p \le 1000, assign the value to .

  if (Cost == 1000) Cost = p;

Store the computed shipping cost in the corresponding cell of the array.

  MinCost[mask] = Cost;
}

For each mask , iterate over all subsets x \sub mask (the mask is a subset of if and only if mask~AND~x equals ). If we subtract the set corresponding to from the set corresponding to , we get the set with mask mask~XOR~x.

For example, if mask = 1101_2 and x = 1001_2, then mask~XOR~x = 100_2, which corresponds to the set-theoretic operation \{1, 3, 4\} \setminus \{1, 4\} = \{3\}.

for (mask = 0; mask < (1 << n); mask++)
  for (x = 0; x < mask; x++)

Recompute the value of MinCost[mask] according to the recurrence relation given in the problem analysis.

    if ((mask & x) == x) 
      MinCost[mask] = min(MinCost[mask],MinCost[mask^x] + MinCost[x]);

Print the answer, which is stored in the cell MinCost[2^n - 1].

printf("%d\n",MinCost[(1 << n) - 1]);

Algorithm implementation with complexity O(3^n)

Declare the constant and the arrays.

#define MAX 0xFFFFFFFF
unsigned int m[15], MinCost[1 << 14];

For a mask , it is necessary to iterate over all its submasks and find the minimum among all possible values

FindCost(sub) + FindCost(mask~XOR~sub)

Due to the symmetry of this sum, it is sufficient to consider only those submasks for which the condition sub \ge mask~XOR~sub holds.

unsigned int FindCost(int mask)
{
  if (MinCost[mask] != MAX) return MinCost[mask];
  // sub перебирает все подмаски маски mask
  for (int sub = (mask - 1) & mask; 
           sub >= mask ^ sub; sub = (sub - 1) & mask)
    MinCost[mask] = 
            min(MinCost[mask], FindCost(sub) + FindCost(mask ^ sub));
  return MinCost[mask];
}

The main part of the program. Set MinCost[mask] = -1 for all masks whose shipping cost for the corresponding set of items has not yet been computed.

memset(MinCost,0xFF,sizeof(MinCost));

Read the input data.

scanf("%d %d",&n,&p);
for(i = 0; i < n; i++) scanf("%d",&m[i]);

If the cost of a certain subset of items does not exceed (specifically, not more, not less, since the case p = 1000 is possible), then the special offer cannot be applied to their shipment. For such subsets, MinCost[mask] can be immediately computed as the sum of the costs of all items. If the sum of item costs is exactly , then we assign MinCost[mask] = p.

Thus, we preliminarily fill in the known minimum costs for small subsets. This increases the efficiency of the program.

for (mask = 0; mask < (1 << n); mask++)
{
  for(Cost = i = 0; i < n; i++)
    if (mask & (1 << i)) Cost += m[i];
  if (Cost == 1000) Cost = p;
  if (Cost <= 1000) MinCost[mask] = Cost;
}

Print the answer. The entire set of items is represented by the mask 2^n - 1, whose binary representation consists of ones.

printf("%u\n",FindCost((1 << n) - 1));