zoukankan      html  css  js  c++  java
  • [USACO09NOV]硬币的游戏 博弈 dp

    LINK : coin game

    这道题 超级经典去年这个时候我就看过题目了 但时至今日还不会/cy 觉得在做比赛的题目的时候少写省选的题目 多做水题多做不难也不简单的题目就好了。

    由于我是真的不会博弈上dp(其实我博弈都不太会...故写这道题的时候没有过多的思考只是草草想了一波状态就直接看题解了发现状态都列错了。

    当我 理解题解中的做法感觉还不是特别的自然故写一篇题解来印证自己的理解。

    这里我写上最初始的思路吧 题目中想让我们两个玩家都选择最优的情况下 第一个玩家最多能获得多少的钱。看起来是一个博弈但是题目中有限制条件前一个玩家取了j个硬币的话后一个玩家最多取2*j个硬币。

    那如何进行这个过程的呢 我们很难去博弈吧因为没有必胜点和必败点这个东西 不论胜败且每次决策紧扣下一次的决策 搜索复杂度超级高。

    开始我也不知道应该选什么取得最优 如果我知道后续的状态 我们从起手的状态转移到最优后续的状态就好了 可是我们并不知道后续的状态?考虑先把后面的东西求出来转移到前面的比较好。

    因为结束之后的状态我们是知道的选完了最后的状态显然是0 就从这个状态转移好了那么就有了状态 f[i]表示剩下i个硬币此时选择的最优状态首先解决的一个问题是由于两个人 都是选取最优的方法 故状态转移显然是一样的我们只要每次转移的时候从对方最优状态之中选择一个对自己最优的状态就好了 故转移到f[n] 由于第一个玩家先选 f[n] 就是我们的答案了 f[i]=max{sum[i]-f[k];}有了这个状态 相信此时对于两个人拥有同一个状态没有什么疑问了吧两个人都是选取最优的方法显然其实对方也是f数组的含义这样就计算好了f当前这个人如果选的话的最优解 显然我们还需要知道选择了多少个方便转移 那么状态空间到此就非常的完善了 f[i][j]表示剩下i个硬币上一次选了j个硬币的最优解 那么显然了状态转移 f[i][j]=max{sum[i]-f[i-k][k]} 其中k属于 1~2*j 然后 答案自然就是f[n][1]了。

    非常的自然 不是么 我觉得难度还是有的 这值得我慢慢的推敲... 相信状态是我唯一的选择的。

    //#include<bits/stdc++.h>
    #include<iostream>
    #include<cmath>
    #include<ctime>
    #include<algorithm>
    #include<cctype>
    #include<utility>
    #include<queue>
    #include<map>
    #include<set>
    #include<bitset>
    #include<deque>
    #include<vector>
    #include<cstdio>
    #include<cstdlib>
    #include<iomanip>
    #include<stack>
    #include<string>
    #include<cstring>
    #define INF 2000000000
    #define ll long long
    #define db double
    #define max(x,y) ((x)>(y)?(x):(y))
    #define min(x,y) ((x)>(y)?(y):(x))
    #define mp(x,y) make_pair(x,y)
    using namespace std;
    char buf[1<<15],*fs,*ft;
    inline char getc()
    {
        return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
    }
    inline int read()
    {
        int x=0,f=1;char ch=getc();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
        return x*f;
    }
    const int MAXN=2010;
    int n;
    int a[MAXN],sum[MAXN];
    int f[MAXN][MAXN];//f[i][j]表示 现在剩下1~i枚金币且上次取了j次当前第一个玩家能取到的最多钱数
    int main()
    {
        //freopen("1.in","r",stdin);
        n=read();
        for(int i=n;i>=1;--i)a[i]=read();
        for(int i=1;i<=n;++i)sum[i]=sum[i-1]+a[i];
        for(int i=1;i<=n;++i)
            for(int j=1;j<=n;++j)
            {
                f[i][j]=f[i][j-1];
                int w=(j<<1)-1;
                if(w<=i)f[i][j]=max(f[i][j],sum[i]-f[i-w][w]);
                ++w;if(w<=i)f[i][j]=max(f[i][j],sum[i]-f[i-w][w]);
            }
        printf("%d
    ",f[n][1]);
        return 0;
    }
    View Code
  • 相关阅读:
    平衡二叉树之RB树
    平衡二叉树之AVL树
    实现哈希表
    LeetCode Median of Two Sorted Arrays
    LeetCode Minimum Window Substring
    LeetCode Interleaving String
    LeetCode Regular Expression Matching
    PAT 1087 All Roads Lead to Rome
    PAT 1086 Tree Traversals Again
    LeetCode Longest Palindromic Substring
  • 原文地址:https://www.cnblogs.com/chdy/p/11429331.html
Copyright © 2011-2022 走看看