IOI'96 - Day 1
Consider the following two-player game played with a sequence of N positive integers (2 <= N <= 100) laid onto a game board. Player 1 starts the game. The players move alternately by selecting a number from either the left or the right end of the sequence. That number is then deleted from the board, and its value is added to the score of the player who selected it. A player wins if his sum is greater than his opponents.
Write a program that implements the optimal strategy. The optimal strategy yields maximum points when playing against the "best possible" opponent. Your program must further implement an optimal strategy for player 2.
PROGRAM NAME: game1
INPUT FORMAT
Line 1: | N, the size of the board |
Line 2-etc: | N integers in the range (1..200) that are the contents of the game board, from left to right |
SAMPLE INPUT (file game1.in)
6 4 7 2 9 5 2
OUTPUT FORMAT
Two space-separated integers on a line: the score of Player 1 followed by the score of Player 2.
SAMPLE OUTPUT (file game1.out)
18 11
思路:sum[i][j]表示i到j的和,f[i][j]表示i到j先取人的最大和。
f[i][j]=max(sum[i][j-1]-f[i][j-1]+shu[j],sum[i+1][j]-f[i+1][j]+shu[i]);
因为先手最优,答案就是确定的,别被题目给蒙住。
/*
ID:nealgav1
LANG:C++
PROG:game1
*/
#include<fstream>
#include<cstring>
using namespace std;
ifstream cin("game1.in");
ofstream cout("game1.out");
const int mm=1000;
int f[mm][mm],sum[mm][mm];
int shu[mm];
int main()
{
int n;
while(cin>>n)
{
for(int i=1;i<=n;i++)cin>>shu[i];
memset(sum,0,sizeof(sum));
memset(f,0,sizeof(f));
for(int i=1;i<=n;i++)
{for(int j=i;j<=n;j++)
sum[i][j]=sum[i][j-1]+shu[j];
f[i][i]=shu[i];
}
for(int i=n-1;i>=1;i--)
{ for(int j=i+1;j<=n;j++)
{
f[i][j]=max(sum[i][j-1]-f[i][j-1]+shu[j],sum[i+1][j]-f[i+1][j]+shu[i]);
}
}
cout<<f[1][n]<<" "<<sum[1][n]-f[1][n]<<"\n";
}
}
Russ Cox
We use dynamic programming to determine, for every possible piece of board that could be left at any point in the game, how many points the best strategy gets for the winner, and how many for the loser.
Let us define best[board] to be the highest score we can hope to get by going first starting with the given board.
If we are looking at a board "a ... b", the best number of points is the maximum of the following:
a + total[... b] - best[... b]
b + total[a ...] - best[a ...]
We use total[board] - best[board] as the best that we can hope to do going second starting with the given board.
If we are looking at the board "a", the best number of points is a.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> #define MAXBOARD 100 int board[MAXBOARD]; /* * best and total are indexed so that (e.g.) best[i, l] refers * to the board of length l starting at place i. */ int total[MAXBOARD][MAXBOARD]; int best[MAXBOARD][MAXBOARD]; int max(int a, int b) { return a > b ? a : b; } void main(void) { FILE *fin, *fout; int j, l, n; fin = fopen("game1.in", "r"); fout = fopen("game1.out", "w"); assert(fin != NULL && fout != NULL); fscanf(fin, "%d", &n); for(j=0; j<n; j++) fscanf(fin, "%d", &board[j]); /* calculate subboard totals */ for(j=0; j<n; j++) total[j][0] = 0; for(l=1; l<=n; l++) for(j=0; j+l<=n; j++) total[j][l] = board[j] + total[j+1][l-1]; /* calc best for boards of size one */ for(j=0; j+1<=n; j++) best[j][1] = board[j]; /* calc best for bigger boards */ for(l=2; l<=n; l++) for(j=0; j+l<=n; j++) best[j][l] = max(board[j] + total[j+1][l-1] - best[j+1][l-1], board[j+l-1] + total[j ][l-1] - best[j ][l-1]); fprintf(fout, "%d %d\n", best[0][n], total[0][n]-best[0][n]); exit(0); }
Another take on game1
Nick Pilkington of South Africa writes:
You only need O(n) space for the sum not O(n*n). This eliminates extra calculation as it can be computed during input. This also means that the board values don't need to be stored at all, leading to a much tighter solution:
#include <fstream> using namespace std; #define min(a,b) ((a<b)?a:b) ifstream fin("game1.in"); ofstream fou("game1.out"); int n; int sum[101]; int best[101][101]; void main() { fin >> n; for(int i = 1; i <= n; i++) { fin >> best[i][i]; sum[i] = sum[i-1] + best[i][i]; } for(int i = 1; i <= n; i++) for(int j = 1; j+i <= n; j++) best[j+i][j] = sum[j+i]-sum[j-1] - min(best[j+i-1][j], best[j+i][j+1]); fou << best[n][1] << " " << sum[n] - best[n][1] << endl; }
More optimizations
Lucian Boca Romania writes:
I propose some memory optimizations for "A Game" problem.
The algorithm is the same, I simulate the calculation of the matrix best[i][l] = the best score wich can be obtained by the first player with the board pieces i,i+1,...,i+l-1 (a sequence of numbers starting with position i and having the length l). I also simulate the calculation of the matrix total[i][l] = the sum of all elements in the sequence starting with position i and having the length l. The reccurence relation is:
best[i][l]=total[i][l] - min( best[i+1][l-1], best[i][l-1] )
and our goal is to obtain best[1][n]
The optimizations:
- You don't need to memorize the board. All the information about the board is in total(i,l)
- You don't need to use a matrix for total(i,l). You calculate a vector t[i]=the sum of elements 1,2,...,i. So, total(i,l) will be t[i+l-1] - t[i-1]
- You don't need to use a matrix NxN, since you only need the last two columns. So, instead of using l, we can use l%2 and allocate the matrix best[NMAX][2]; the reccurence relation becomes best[i][l%2]=total(i,l) - min( best[i+1][(l-1)%2], best[i][(l-1)%2]) and our goal is to obtain best[1][n%2].
Here's the code:
#include <stdio.h> #define NMAX 101 int best[NMAX][2], t[NMAX]; int n; void readx () { int i, aux; freopen ("game1.in", "r", stdin); scanf ("%d", &n); for (i = 1; i <= n; i++) { scanf ("%d", &aux); t[i] = t[i - 1] + aux; } fclose (stdin); } inline int min (int x, int y) { return x > y ? y : x; } void solve () { int i, l; for (l = 1; l <= n; l++) for (i = 1; i + l <= n + 1; i++) best[i][l%2] = t[i + l - 1] - t[i - 1] - min (best[i + 1][(l - 1) % 2], best[i][(l - 1) % 2]); } void writex () { freopen ("game1.out", "w", stdout); printf ("%d %d\n", best[1][n % 2], t[n] - best[1][n % 2]); fclose (stdout); } int main () { readx (); solve (); writex (); return 0; }
USER: Neal Gavin Gavin [nealgav1]
TASK: game1
LANG: C++
Compiling...
Compile: OK
Executing...
Test 1: TEST OK [0.011 secs, 11172 KB]
Test 2: TEST OK [0.011 secs, 11172 KB]
Test 3: TEST OK [0.011 secs, 11172 KB]
Test 4: TEST OK [0.011 secs, 11172 KB]
Test 5: TEST OK [0.011 secs, 11172 KB]
Test 6: TEST OK [0.011 secs, 11172 KB]
Test 7: TEST OK [0.011 secs, 11172 KB]
Test 8: TEST OK [0.011 secs, 11172 KB]
Test 9: TEST OK [0.011 secs, 11172 KB]
Test 10: TEST OK [0.011 secs, 11172 KB]
Test 11: TEST OK [0.011 secs, 11172 KB]
Test 12: TEST OK [0.011 secs, 11172 KB]
Test 13: TEST OK [0.011 secs, 11172 KB]
Test 14: TEST OK [0.011 secs, 11172 KB]
Test 15: TEST OK [0.011 secs, 11172 KB]
Test 16: TEST OK [0.011 secs, 11172 KB]
All tests OK.
YOUR PROGRAM ('game1') WORKEDFIRST TIME! That's fantastic
-- and a rare thing. Please accept these special automated