Round2_20045_solution

advertisement
Solution: Strange Coins
There are a number of different ways to solve this problem. We will run through a
few of them before we discuss what the fastest way to solve the problem might be.
Let’s assume that the present costs exactly 1000 Olympios.
(a) One of the first things you might try doing is to think about the different test cases
that might be used to trick you program. A very simple case might be if only one coin
was needed.
Coins = {10, 50, 99, 100, 76, 194, 32, 1000, 7, 995}
Here, the 1000 coin will be enough. So the answer is one.
(b) Another test case might need all the coins. So the total sum of coins is 1000.
Coins = {10, 10, 10, 20, 50, 700, 100, 99, 1}
So the answer is 9 (nine coins needed).
(c) Another test might include coins of value larger than the gift price. If the gift price
was 100, there might be a case like this:
Coins = {200, 250, 101, 111, 217}
In a case like that, we can ignore coins of value greater than the price of the present.
(Incidentally, the example above has no solution)
It would seem from examples (a) and (b) that the solution can be anything from one
coin, up to N coins (where N is the total number of coins in his pocket). So our
program could try all the combinations of coins to see if any of them added up to the
exact price.
Well, how many combinations are there? For each combination, we will choose a
subset of the N coins. For each coin in his pocket, Lionel can choose to either include
it in the subset or not. That means he has 2 choices with each coin. This gives a total
of 2^N (2 to the power of N) combinations.
For example, if Coins = {1,2,7}
N = 3 coins
We have 2^3 = 8 possible combinations [(no coins = 0), (1), (1,2), (1,7), (2), (2,7),
(1,2,7), (7)]
When there are 40 coins in his pocket, that will mean 2^40 combinations.
2^40 = 1,099,511,627,776 !!!
Ok, so our program that checks all combinations wont work for the big test cases, but
it might get us some points. So how would we go about writing a program to try all
the possibilities?
One simple way is to use ‘recursion’. Let’s keep an array called
// This array indicates if a particular coin is being used in our final total
int used[40]
// This array stores the value of each coin
int value[40]
// This stores our current best solution for the minimum number of coins
int bestmin = 999;
// This is the present cost
int target;
int calculate_total(void) {
int i, total = 0;
for (i=0 ; i<N ; i++)
if (used[i] == 1)
total = total + value[i];
return total;
}
int count_coins(void) {
int i, total = 0;
for (i=0 ; i<N ; i++)
if (used[i] == 1)
total++;
return total;
}
void find_min(int curr) {
if (calculate_total() == target)
bestmin = count_coins();
if (curr >= N)
return;
used[curr] = 0;
find_min(curr+1);
used[curr] = 1;
find_min(curr+1);
}
A lot of the initialisation and data input code has been left out but you can hopefully
see the core of the technique. The ‘find_min’ function is the bit that does all the work.
It is a ‘recursive’ function, which means that it calls itself!
Ok, so let’s see how we might come to an effective solution. We know the limits on
the values of the coins (40) and the number of coins (250). So our maximum total is
40x250 = 10,000. If we keep an array of size 10,000 and in it we will store ‘1’ at
position k if it is possible to use the coins we have to add up to exactly k Olympios,
‘0’ if not. To construct this array, we initially set all values to 0. Then we read in each
coin value, v, in turn and add this value to every k that has a ‘1’ in the array. So if
there is a ‘1’ at position k, set position k+v to ‘1’. Also, set position ‘v’ to ‘1’. Let’s
say our coins are {2, 7, 1}. On the next page, we see three rows:
(a) k value
(b) got_total array
(c) min_coins array
Initial array:
(a)1 2
(b)0 0
(c)99 99
3
0
99
4
0
99
5
0
99
6
0
99
7
0
99
8
0
99
9
0
99
10
0
99
11
0
99
12
0
99
Read in 2
(a)1 2
(b)0 1
(c)99 1
3
0
99
4
0
99
5
0
99
6
0
99
7
0
99
8
0
99
9
0
99
10
0
99
11
0
99
12
0
99
Read in 7
(a)1 2
(b)0 1
(c)99 1
3
0
99
4
0
99
5
0
99
6
0
99
7
1
1
8
0
99
9
1
2
10
0
99
11
0
99
12
0
99
read in 1
(a)1 2
(b)1 1
(c)1 1
3
1
2
4
0
99
5
0
99
6
0
99
7
1
1
8
1
2
9
1
2
10
1
3
11
0
99
12
0
99
So now we know what totals are possible with our coins (from the (b) rows). We still
need to know what the minimum number of coins is to get each total. To solve this,
we use another array which will store the minimum number of coins; initialised to 99
(or any big number greater than N). This is the array shown in (c) above. This
technique is based on ‘dynamic programming’, a method that used pre-calculated
information to do things faster. The algorithm is shown below:
Initialise arrays
Read in value, v
For k = 10001 downto 1
if (got_total[k] == 1)
Set got_total[k+v] = 1
Set min_coins[k+v] = min(min_coins[k+v], min_coins[k]+1);
Set got_total[v] = 1
Set min_coins[v] = 1
It’s important to go backwards in the ‘for’ loop, because otherwise we might add the
same coin twice.
Solution: The King-Maker
In this problem there a few things we can notice to make the solution simpler. First, if
two factions support the same candidate, we can combine them into one. For example,
if part of the input is:
20 13 10
20 8 7
We can combine that into one faction:
20 21 17
(i.e. If you don’t elect candidate 20, we will attack you with 21 tanks, if you do elect
them, we will aid you with 17 tanks)
We start by assuming we have not chosen anyone for the positions – so everyone is
against us!
Example input:
5
1 10 10
2 20 10
3 5 10
463
573
So there are (10+20+5+6+7 =) 48 tanks opposing us. Let’s give us a starting score of
-48. Now, for each candidate, we can calculate how this score will change if we pick
them. For example, for candidate 1, if we choose this person, our score will go up by
20 (10 less tanks opposing, 10 more with us). So, a candidate’s score is simply the
sum of their ‘opposing’ + ‘supporting’ values. After calculating the scores, we then
just need to pick the top 3 candidates. If two candidates have the same score, simply
choose the candidate with the lower ID. This will ensure that the ‘sum of candidates
IDs’ is minimised if there are multiple solutions.
The algorithm is quite short:
// candidates and tank numbers
int C[30000], J[30000], K[30000];
// number of statements
int S;
int score;
// candidate score
int cand[10000];
// starting score
score=0;
for (i=0 ; i<S ; i++)
score = score – J[i];
// initialise array
for (i=0 ; i<10000 ; i++)
cand[i] = 0;
// Note: we use C[i]-1 because arrays start from zero
for (i=0 ; i<S ; i++)
cand[C[i]-1] += (J[i] + K[i]);
All that’s left now is to choose the 3 candidates with the highest scores.
Solution: Prime Factors
All we need to do here is to count the number of prime factors for every number and
store the one with the most prime factors (and if two of them have the same number
of prime factors, choose the smallest).
So how do we count the prime factors of a number?
The simplest way is to try each prime and if it divides the number, then it’s a factor!
So how do we calculate the primes? There are a number of ways to do it, but here we
can do a brute force approach because we are not using very big numbers.
To calculate the primes
int nprimes;
int primes[1024];
nprimes = 2;
primes[0] = 2;
for (i=3 ; i<1024 ; i++) {
isprime = true;
for (j=0 ; j<nprimes ; j++)
if ((i % primes[j]) == 0) {
isprime = false;
break;
}
if (isprime) {
primes[nprimes] = I;
nprimes++;
}
}
To count the number of prime factors:
count = 0;
For (i=0 ; i<nprimes ; i++)
If ((num % primes[i]) == 0)
count++;
printf(“%d has %d prime factors\n”, num, count);
A more efficient way to do this problem could be to remember that the input numbers
will never be more than 1024. You may know that any non-prime will have a prime
factor that is less or equal to than the square root of itself (and if you don’t … now
you do!). So any non-prime between 2 and 1024 will have a prime factor less than or
equal to sqrt(1024) = 32. So this list of primes is {2,3,5,7,11,13,17,19,23,29,31}.
Some examples: Let’s take 407. If we try to divide these primes into 407, we find that
11 is a factor. 407 / 11 = 37. Since 37 is not divisible by any of the primes, it must be
prime. So 407 has 2 prime factors. Take 961; 31 divides it. 961/31 = 31. Now, 31 can
be divided by 31: 31/31 = 1. So it also has 2 prime factors. Take 997. It cannot be
divided by any of the primes in the list, so it must be prime itself. Therefore, 997 has
one prime factor. This improvement in efficiency is not significant in this problem
and is also tricky to code (to get it right). In problems like this, I would recommend
the reliable but inefficient algorithm because you can otherwise waste precious time
debugging.
Solution: Above Average
In this problem, we have to read through the list twice (first to calculate the average,
then second to count how many are above it). To calculate the average use a variable
‘count’ to add up all the numbers. Then divide it by the total number of numbers. It
doesn’t matter that it is rounded down to the nearest integer. One the next pass, set a
counter to zero, then simply compare every number to the average and if it’s greater,
then increment your counter.
Code shown below:
#include <stdio.h>
int main(void) {
int score[10000];
// open input file
FILE *fin = fopen("average.in", "rt");
if (fin == NULL) {
fprintf(stderr, "File not found: Average.in\n");
return -1;
}
// read input file
// and calculate total sum
int i, N, sum;
fscanf(fin, "%d", &N);
sum=0;
for (i=0 ; i<N ; i++) {
fscanf(fin, "%d", &score[i]);
sum += score[i];
}
fclose(fin);
// calculate average
int average = sum / N;
// count above average
int count = 0;
for (i=0 ; i<N ; i++) {
if (score[i] > average)
count++;
}
// output count
FILE *fout = fopen("average.out", "wt");
fprintf(fout, "%d\n", count);
fclose(fout);
return 0;
}
Download