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]));
    }
  • 相关阅读:
    Table.Combine追加…Combine(Power Query 之 M 语言)
    Table.NestedJoin合并…Join(Power Query 之 M 语言)
    Delphi Post登陆Delphi盒子论坛源码
    分享一个函数GetStr
    用友生产工艺路线导入工具1.1
    笔下文学小说下载【3.01】 发布
    U6 3.2盘点单管理1.01
    防止SQL SERVER的事件探查器跟踪软件
    双系统的安装(原创)
    2345导航自动登陆签到【1.01】
  • 原文地址:https://www.cnblogs.com/wutongtong3117/p/7673038.html
Copyright © 2011-2022 走看看