zoukankan      html  css  js  c++  java
  • UVA 10891 Game of Sum DP

    题意:

    给n个数,A,B两人轮流取,每次可从左右其中一边取1或多个数,两个人都尽量使自己取的数总和最大,问最终sumA-sumB最大为多少。

    由于两个人取的数总和是一定的,所以只要求出先取的那个最多有多少就行了。

    用dp[i][j]表示a[i..j]这一段数中先取的人能取到的最大和。(这里的先取意思是表示这在一段先取,不是A的意思)

    先取的人可能取左边任意一段a[i..i],a[i..i+1],a[i...i+2]....a[i,j]或右边任意一段a[j...j],a[j-1...j],a[j-2..j]....a[i,j]

    那么另一个人就有可能从a[i+1...j]、a[i+2....j],a[j..j],0,a[i..i],a[i...i+1],a[i..i+2]...a[i...j-1]中取。0表示已经被取完,也就是边界。

    要让dp[i][j]最大,最要让另一个人从剩下的数中能取到的最大值最小

    dp[i][j]=sum[i][j]-min{ dp[i+1][j] , dp[i+2][j] , ...  dp[j][j] , 0 , dp[i][i+1] , dp[i][i+2] , dp[i][j-1] };

     状态数是n^2,每个状态的转移有2n个,所以总复杂度为O(n^3)对于这题的n<=100的数据量已经足够

    最终的结果就是dp[1][n]-(sum[1][n]-dp[1][n])=2*dp[1][n]-sum[1][n];

    int da[110];
    int sum[110];
    int dp[110][110];
    int vis[110][110];
    int f(int l,int r)
    {
        if(vis[l][r])return dp[l][r];
        vis[l][r]=1;
        int m=0;
        for(int i=l;i<r;i++)
        {
            m=min(m,f(l,i));
        }
        for(int i=r;i>l;i--)
        {
            m=min(m,f(i,r));
        }
        return dp[l][r]=sum[r]-sum[l-1]-m;
    }
    int main()
    {
        int n;
        while(scanf("%d",&n)!=EOF&&n)
        {
            sum[0]=0;
            for(int i=1;i<=n;i++)
            {
                scanf("%d",&da[i]);
                sum[i]=sum[i-1]+da[i];
            }
            memset(vis,0,sizeof(vis));
            printf("%d
    ",2*f(1,n)-sum[n]);
        }
        return 0;
    }
    View Code

    但是还能把状态的转移优化
    设f[i][j]=min{ f[i][k] | i<=k<=j }; g[i][j] = min{ g[k][j] | i<=k<=j };

    那么dp[i][j] = sum[i][j] - min{ f[i][j-1] , g[i+1][j] , 0}

    f[i][j]和g[i][j]也可以同样递推得到

    复杂度便降到O(n^2)

    int da[110];
    int sum[110];
    int dp[110][110];
    int g[110][110];
    int f[110][110];
    int main()
    {
        int n;
        while(scanf("%d",&n)!=EOF&&n)
        {
            sum[0]=0;
            for(int i=1;i<=n;i++)
            {
                scanf("%d",&da[i]);
                sum[i]=sum[i-1]+da[i];
            }
            for(int len=0;len<n;len++)
            {
                for(int i=1;i+len<=n;i++)
                {
                    if(len>0)
                    {
                        int m;
                        int j=i+len;
                        m=min(f[i][j-1],g[i+1][j]);
                        m=min(m,0);
                        dp[i][j]=sum[j]-sum[i-1]-m;
                        f[i][j]=min(f[i][j-1],dp[i][j]);
                        g[i][j]=min(g[i+1][j],dp[i][j]);
                    }
                    else
                    {
                        dp[i][i]=da[i];
                        f[i][i]=da[i];
                        g[i][i]=da[i];
                    }
                }
            }
            printf("%d
    ",2*dp[1][n]-sum[n]);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    jquery $.getJSON()跨域请求
    JQuery 字符串转时间格式
    php Function ereg() is deprecated的解决方法
    ucenter 验证码看不到的解决办法
    C#通过UserAgent判断智能设备(Android,IOS,PC,Mac)
    gpio_irq出现错误genirq: Setting trigger mode 6 for irq 168 failed (gpio_set_irq_type+0x0/0x230)
    驱动模块的加载(linux4.1.15)!
    电压环控制逻辑!
    用电阻检测大电流时2线电阻的PCB画法。
    比较两点压差(比如两点温度相差太大),超过范围,做出动作!
  • 原文地址:https://www.cnblogs.com/BMan/p/3249753.html
Copyright © 2011-2022 走看看