zoukankan      html  css  js  c++  java
  • UVa10891 Game of Sum(dp)

    简述:
    n的序列,两人轮流从两端取数,
    两人都选择最优策略,求两人的得分之差

    分析:
    一开始觉得是一道博弈
    但是可以用dp解决的
    这就和Tyvj上的硬币游戏有异曲同工之妙

    因为只能取一个连续的区间
    所以剩下的一定是原序列中的一个连续区间
    这就指引我们往区间dp的方向上想
    设计状态:f[i][j]表示现在剩下i~j的序列,先手的最高得分
    f[i][j]=sum(i,j)-min{f[i+1][j],f[i+2][j],…,f[j][j],f[i][j-1],f[i][j-2],…,f[i][i],0}
    注意,0表示先手全部取走
    最后答案是
    f[1][n]-(sum(1,n)-f[1][n])

    时间复杂度O(n^3)

    tip

    当面前只有一个数的时候,必须取

    //这里写代码片
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    
    using namespace std;
    
    const int INF=1e9;
    const int N=110;
    int n;
    int sum[N];
    int f[N][N];
    
    void dp()
    {
        int i,j,k;
        memset(f,0,sizeof(f));
        for (int i=1;i<=n;i++) f[i][i]=sum[i]-sum[i-1];   //必须取数 
        for (int i=n;i>=1;i--)
            for (int j=i+1;j<=n;j++)
            {
                int minn=0;
                for (k=i;k<j;k++)
                {
                    minn=min(minn,f[i][k]);
                    minn=min(minn,f[k+1][j]);
                }
                f[i][j]=sum[j]-sum[i-1]-minn;
            }
        printf("%d
    ",f[1][n]-(sum[n]-f[1][n]));
    }
    
    int main()
    {
        scanf("%d",&n);
        while (n)
        {
            sum[0]=0;
            for (int i=1;i<=n;i++) scanf("%d",&sum[i]),sum[i]+=sum[i-1];
            dp();
            scanf("%d",&n);
        }
        return 0;
    }

    然而

    我们还可以让时间复杂去更优一点
    我们发现转移的时候,min值的计算是很有规律的
    所以我们记
    g1[i][j]=min{f[i][j],f[i+1][j],f[i+2][j],…,f[j][j]}
    g2[i][j]=min{f[i][j],f[i][j-1],f[i][j-2],…,f[i][i]}

    转移就变成了:
    f[i][j]=sum(i,j)-min{g1[i+1][j],g2[i][j-1],0}
    不要忘了0

    g1和g2的转移也很好维护
    g1[i][j]=min(g1[i+1][j],f[i][j])
    g2[i][j]=min(g2[i][j-1],f[i][j])

    这样优化之后,时间复杂度就降到了n^2

    void dp()
    {
        int i,j,k;
        for (int i=1;i<=n;i++) 
        {
            f[i][i]=sum[i]-sum[i-1];
            g1[i][i]=f[i][i];
            g2[i][i]=f[i][i];
        }
        for (i=n;i>=1;i--)
            for (j=i+1;j<=n;j++)
            {
                int minn=0;
                minn=min(minn,g1[i+1][j]);
                minn=min(minn,g2[i][j-1]);
                f[i][j]=sum[j]-sum[i-1]-minn;
                g1[i][j]=min(g1[i+1][j],f[i][j]);
                g2[i][j]=min(g2[i][j-1],f[i][j]);
            }
        printf("%d
    ",f[1][n]-(sum[n]-f[1][n]));
    }
  • 相关阅读:
    linux的一些命令
    Java中小数精确计算
    java中基本数据类型和包装类自动装箱和拆箱
    Python学习day14(内置函数二,匿名函数)
    Python学习day13(内置函数一)
    Python学习day12(生成器,列表/生成器推导式)
    Python学习day11(函数名本质,闭包及迭代器)
    Python学习day10(函数名称空间及嵌套)
    Python学习day9(函数初识)
    Python学习day8(文件操作)
  • 原文地址:https://www.cnblogs.com/wutongtong3117/p/7673037.html
Copyright © 2011-2022 走看看