uva 10891 dp类似博弈

来源:互联网 发布:黑马程序员28期网盘 编辑:程序博客网 时间:2024/05/16 06:25

http://vjudge.net/contest/view.action?cid=53516#problem/C

Description

Download as PDF

Problem E
Game of Sum
Input File: 
e.in

Output: Standard Output

 

This is a two player game. Initially there are n integer numbers in an array and players A and B get chance to take them alternatively. Each player can take one or more numbers from the left or right end of the array but cannot take from both ends at a time. He can take as many consecutive numbers as he wants during his time. The game ends when all numbers are taken from the array by the players. The point of each player is calculated by the summation of the numbers, which he has taken. Each player tries to achieve more points from other. If both players play optimally and player A starts the game then how much more point can player A get than player B?

Input

The input consists of a number of cases. Each case starts with a line specifying the integer n (0 < n ≤100), the number of elements in the array. After that, n numbers are given for the game. Input is terminated by a line where n=0.

 

Output

For each test case, print a number, which represents the maximum difference that the first player obtained after playing this game optimally.

 

Sample Input                                Output for Sample Input

4

4 -10 -20 7

4

1 2 3 4

0

7

10

题目大意:(大白书p67)

                   有一个长度为n的整数序列,两个游戏者A和B轮流取数,A先取。每次玩家只能从左端或者右端取走任意数量的个数,但不能两端都取。所有的数都被取走则游戏结束,然后统计每个人取走的所有数之和,作为各自的得分。两个人才采取的策略都是让自己的得分尽量高,并且两个人都足够聪明,求A的得分减去B的得分后的结果。

解题思路:d(i,j)表示原序列的第i~j个元素组成的子序列(1~n)在双方都采用最优策略的情况下,先手得分的最大值

状态转移方程d(i,j)=sum(i,j)-min(d(i+1,j),d(i+2,j).......d(j,j),d(i,j-1).....d(i,j-2)......d(i,i),0);//0表示取完所有的数。

记忆化搜索来写(状态有n*n个,每个状态有n个转移,所以时间复杂度O(n^3))

#include <stdio.h>#include <string.h>#include <iostream>using namespace std;const int maxn=100+10;int s[maxn],a[maxn],d[maxn][maxn],vis[maxn][maxn],n;int dp(int i,int j){        if(vis[i][j])            return d[i][j];        vis[i][j]=1;        int m=0;        for(int k=i+1;k<=j;k++)            m=min(m,dp(k,j));        for(int k=i;k<j;k++)            m=min(m,dp(i,k));        d[i][j]=s[j]-s[i-1]-m;        return d[i][j];}int main(){    while(~scanf("%d",&n))    {        if(n==0)            break;        s[0]=0;        for(int i=1;i<=n;i++)        {            scanf("%d",&a[i]);            s[i]=s[i-1]+a[i];        }        memset(vis,0,sizeof(vis));        //printf("[%d]\n",dp(1,n));        printf("%d\n",2*dp(1,n)-s[n]);    }    return 0;}

进一步优化:令f(i,j)=min{ d(i,j), d(i+1,j),......d(j,j), },g(i,j)=min{ d(i,j), d(i,j-1),.....d(i,i) },那么状态转移方程就是 d(i,j)=sum(i,j)-min(   f(i+1,j),  g(i,j-1),0  );

f 和g也可以快速递推出来,f(i,j)=min{d(i,j),f(i+1,j)}; g(i,j)=min{d(i,j),g(i,j-1)};因此每个f(i,j)计算的时间都将为了O(1),整个算法就降到了O(n^2)

#include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> using namespace std; const int maxn=100+10; int s[maxn],a[maxn],d[maxn][maxn],f[maxn][maxn],g[maxn][maxn],n; int main() {     while(~scanf("%d",&n))     {         if(n==0)            break;         s[0]=0;         for(int i=1;i<=n;i++)         {            scanf("%d",&a[i]);            s[i]=s[i-1]+a[i];         }         for(int i=1;i<=n;i++)            f[i][i]=g[i][i]=d[i][i]=a[i];//边界         for(int l=1;l<n;l++)//按照L=j-i递增的顺序计算            for(int i=1;i+l<=n;i++)            {                int j=i+l;                int m=0;                   m=min(m,f[i+1][j]);                m=min(m,g[i][j-1]);                d[i][j]=s[j]-s[i-1]-m;                f[i][j]=min(d[i][j],f[i+1][j]);//递推f和g                g[i][j]=min(d[i][j],g[i][j-1]);            }        printf("%d\n",2*d[1][n]-s[n]);     }     return 0; }


0 0
原创粉丝点击