USACO section 3.3 A Game(DP)

53 篇文章 0 订阅
A Game
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";
  }
}


 

A Game
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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值