Here, we will discuss the solution to Backpacking from AIO 2024.
In this subtask, it is always cheaper to buy food in earlier towns. Ideally, we would buy all the food in town 1. However, this may not be possible, depending on the size of our backpack. Instead, at each town, we either:
# Open the input and output files.
input_file = open("backin.txt", "r")
output_file = open("backout.txt", "w")
# Read the values of N, K, D, and C from the input file.
input_line = input_file.readline().strip()
N, K = map(int, input_line.split())
input_line = input_file.readline().strip()
D = list(map(int, input_line.split()))
input_line = input_file.readline().strip()
C = list(map(int, input_line.split()))
answer = 0
need_to_buy = sum(D)
curr_backpack = 0
for i in range(N-1):
# K-curr_backpack is the maximum number of cans we can fit in our backpack
# need_to_buy is the number of extra cans we need to reach the final town
to_buy = min(K-curr_backpack, need_to_buy)
curr_backpack += to_buy
need_to_buy -= to_buy
answer += to_buy * C[i]
# We walk from town i to town i+1, eating D[i] cans
curr_backpack -= D[i]
# Write the answer to the output file.
output_file.write("%d\n" % (answer))
# Finally, close the input/output files.
input_file.close()
output_file.close()
#include <cstdio>
#include <algorithm>
int N, K, D[200005], C[200005];
int main(void) {
/* Open the input and output files. */
FILE *input_file = fopen("backin.txt", "r");
FILE *output_file = fopen("backout.txt", "w");
/* Read the values of N, K, D, and C from the input file. */
fscanf(input_file, "%d%d", &N, &K);
for (int i = 0; i < N-1; i++) {
fscanf(input_file, "%d", &D[i]);
}
for (int i = 0; i < N; i++) {
fscanf(input_file, "%d", &C[i]);
}
int answer = 0;
int need_to_buy = 0;
for (int i = 0; i < N-1; i++) {
need_to_buy += D[i];
}
int curr_backpack = 0;
for (int i = 0; i < N-1; i++) {
// K-curr_backpack is the maximum number of cans we can fit in our backpack
// need_to_buy is the number of extra cans we need to reach the final town
int to_buy = std::min(K-curr_backpack, need_to_buy);
curr_backpack += to_buy;
need_to_buy -= to_buy;
answer += to_buy * C[i];
// We walk from town i to town i+1, eating D[i] cans
curr_backpack -= D[i];
}
/* Write the answer to the output file. */
fprintf(output_file, "%d\n", answer);
/* Finally, close the input/output files. */
fclose(input_file);
fclose(output_file);
}
In this subtask, our backpack is big enough to fit all the cans we will ever need:
Consider an example with 7 towns that have costs 4, 6, 3, 5, 1, 2, 3:
Unfortunately, an algorithm with a time complexity of O(N2) is too slow for this subtask. Luckily, with some careful planning, we can do this process in O(N) time.
# Open the input and output files.
input_file = open("backin.txt", "r")
output_file = open("backout.txt", "w")
# Read the values of N, K, D, and C from the input file.
input_line = input_file.readline().strip()
N, K = map(int, input_line.split())
input_line = input_file.readline().strip()
D = list(map(int, input_line.split()))
input_line = input_file.readline().strip()
C = list(map(int, input_line.split()))
distance_to_end = sum(D)
# We start by buying every can at town 1
price_paid = C[0]
answer = price_paid*distance_to_end
for i in range(N-1):
if C[i] < price_paid:
# C[i] is the cheapest town so far
# If we bought the remaining cans from this town,
# we would save price_paid-C[i] dollars per can
answer -= (price_paid-C[i]) * distance_to_end
price_paid = C[i]
distance_to_end -= D[i]
# Write the answer to the output file.
output_file.write("%d\n" % (answer))
# Finally, close the input/output files.
input_file.close()
#include <cstdio>
#include <algorithm>
int N, K, D[200005], C[200005];
int main(void) {
/* Open the input and output files. */
FILE *input_file = fopen("backin.txt", "r");
FILE *output_file = fopen("backout.txt", "w");
/* Read the values of N, K, D, and C from the input file. */
fscanf(input_file, "%d%d", &N, &K);
for (int i = 0; i < N-1; i++) {
fscanf(input_file, "%d", &D[i]);
}
for (int i = 0; i < N; i++) {
fscanf(input_file, "%d", &C[i]);
}
int distance_to_end = 0;
for (int i = 0; i < N-1; i++) {
distance_to_end += D[i];
}
int price_paid = C[0];
int answer = price_paid*distance_to_end;
for (int i = 0; i < N-1; i++) {
if (C[i] < price_paid) {
// C[i] is the cheapest town so far
// If we bought the remaining cans from this town,
// we would save price_paid-C[i] dollars per can
answer -= (price_paid-C[i]) * distance_to_end;
price_paid = C[i];
}
distance_to_end -= D[i];
}
/* Write the answer to the output file. */
fprintf(output_file, "%d\n", answer);
/* Finally, close the input/output files. */
fclose(input_file);
fclose(output_file);
}
This problem has many different solutions and we will present just one of them. Our solution combines some of the ideas from subtasks 1 and 2. We consider the towns one at a time, in order. At each town, we either:
See below for an example of this solution.
Similarly to Shopping Spree, we can use an exchange argument to prove that this greedy algorithm is correct:
What is the time complexity of this solution?
# distance_to_town[i] will store the distance from town 0 to town i (that is, D[0] + D[1] + ... + D[i-1])
distance_to_town = []
# firstCheaperAfter[i] will contain the index of the first town after i that is cheaper than i (or N-1) if no town is cheaper than i
firstCheaperAfter = []
# Open the input and output files.
input_file = open("backin.txt", "r")
output_file = open("backout.txt", "w")
# Read the values of N, K, D, and C from the input file.
input_line = input_file.readline().strip()
N, K = map(int, input_line.split())
input_line = input_file.readline().strip()
D = list(map(int, input_line.split()))
input_line = input_file.readline().strip()
C = list(map(int, input_line.split()))
answer = 0
# Compute the distance_to_town list
distance_to_town.append(0)
for i in range(1, N):
distance_to_town.append(distance_to_town[i-1] + D[i-1])
# Compute the firstCheaperAfter array
for i in range(N):
firstCheaperAfter.append(N-1)
for j in range(i+1, N):
if C[j] < C[i]:
firstCheaperAfter[i] = j
break
in_backpack = 0 # in_backpack stores the number of cans in our backpack at the current moment
for i in range(N):
distance_to_cheaper_town = distance_to_town[firstCheaperAfter[i]] - distance_to_town[i]
# our backpack can hold at most K cans
distance_to_cheaper_town = min(distance_to_cheaper_town, K)
# we need distance_to_cheaper_town cans to reach the next cheaper town, however we already have in_backpack
# therefore distance_to_cheaper_town-in_backpack is the number of extra cans we want to buy
# this value can be negative, which is why we max it with 0
will_buy = max(0, distance_to_cheaper_town - in_backpack)
answer += will_buy * C[i]
in_backpack += will_buy
# we now walk to the next town, eating D[i] cans
if i < N-1:
in_backpack -= D[i]
# Write the answer to the output file.
output_file.write("%d\n" % (answer))
# Finally, close the input/output files.
input_file.close()
output_file.close()
#include <cstdio>
#include <algorithm> // for std::min and std::max
int N, K, D[200005], C[200005];
/*
* distance_to_town[i] will store the distance from town 0 to town i (that is, D[0] + D[1] + ... + D[i-1])
*/
int distance_to_town[200005];
/*
* firstCheaperAfter[i] will contain the index of the first town after i that is cheaper than i (or N-1) if no town is cheaper than i
*/
int firstCheaperAfter[200005];
int answer;
int main(void) {
/* Open the input and output files. */
FILE *input_file = fopen("backin.txt", "r");
FILE *output_file = fopen("backout.txt", "w");
/* Read the values of N, K, D, and C from the input file. */
fscanf(input_file, "%d%d", &N, &K);
for (int i = 0; i < N-1; i++) {
fscanf(input_file, "%d", &D[i]);
}
for (int i = 0; i < N; i++) {
fscanf(input_file, "%d", &C[i]);
}
// Compute the distance_to_town array
for (int i = 1; i < N; i++) {
distance_to_town[i] = distance_to_town[i-1] + D[i-1];
}
// Compute the firstCheaperAfter array
for (int i = 0; i < N; i++) {
firstCheaperAfter[i] = N-1;
for (int j = i+1; j < N; j++) {
if (C[j] < C[i]) {
firstCheaperAfter[i] = j;
break;
}
}
}
int in_backpack = 0; // in_backpack stores the number of cans in our backpack at the current moment
for (int i = 0; i < N; i++) {
int distance_to_cheaper_town = distance_to_town[firstCheaperAfter[i]] - distance_to_town[i];
// our backpack can hold at most K cans
distance_to_cheaper_town = std::min(distance_to_cheaper_town, K);
// we need distance_to_cheaper_town cans to reach the next cheaper town, however we already have in_backpack
// therefore distance_to_cheaper_town-in_backpack is the number of extra cans we want to buy
// this value can be negative, which is why we max it with 0
int will_buy = std::max(0, distance_to_cheaper_town - in_backpack);
answer += will_buy * C[i];
in_backpack += will_buy;
// we now walk to the next town, eating D[i] cans
in_backpack -= D[i];
}
/* Write the answer to the output file. */
fprintf(output_file, "%d\n", answer);
/* Finally, close the input/output files. */
fclose(input_file);
fclose(output_file);
return 0;
}
We can use the same greedy algorithm as subtask 3, but we must improve the time complexity. For each town i, we need to find the closest town after i that is cheaper than town i. This is the slow part of our solution. We can speed it up by utilising the constraint that 1 ≤ Ci ≤ 20 for all i: