qbztのday6午前

合併や石が、これは、任意の2つのマージも、ほとんどとXORを行うことができます

 

F [S]の組み合わせ砂利パイルに対応する最小コストを表します

最後[2 ^ N-1] fを求めて

どのように転送?

前回は2つの山も山にマージし、しかし、すべての部分集合Sを列挙することができ、多くの例があるでしょう、セットと残りの合併を扱います

直接の場合はsが見るよりも小さい列挙子| =秒またはS&A = A

O(4 ^ N)の複雑

最適化:彼らは直接場合列挙サブセットがより速くなるでしょう。

cinを>> N;
    INT A = 0 ; <N; ++ 
        CIN >> Z [A]。
    INT A = 0 ; <(1 << N); ++ のためのINT B = 0、B <N; B ++ 場合(A&(1 << b)参照)
                の和[A] + = Z [ B]。
    
    memsetの(F、0x3fをはsizeof (F))。
    INT A = 0 <N; ++
        F [ 1 << A] = 0 ; 
        
    / * のための(INT S = 0; S <(1 << N); S ++)
        のための(INT A = 1; <S; ++)
            IF((S | A)== S)
                [s]は、F =分(F [S]、[A] + F F [A ^ S] +(和[A] ^和[A ^ S]))。* / 
                
    INT S = 0 ; S <(1 << N); S ++ のためのINT A =(S- 1)&S; =(A- 1)&S)
            [s]は、F =分(F [S]、[A] + F F [A ^ S] +(和[A] ^和[A ^ S]))。
                
    COUT << [(F 1 << N) - 1 ] << ENDL。

 

 

O(3 ^ N)の複雑

なお、列挙法のサブセット(非常に重要)

 

ゲーム理論DP

 

そこのゲームGがあり、二人は(van♂)、ターンベース果たし、法の結果との間の区別をしない彼が失われたとき、人が操作することができないときということです

 

例:

、整数S(2 <= S <= 200)、上側のハンドマイナスSの数値xを有し、少なくとも1、しかしS.未満であります 双方は正の整数を引くSへの交代が、k倍にお互いを失う前のラウンドの数を超えることはできません後、当事者が0勝に減少しました。彼は、上の手が勝つかどうかを尋ねましたか?

どのようにDP?

特定の動作状態で、状態が別の状態に遷移することができればGには、ゲーム状態をたくさん持っています。ステータスが転送できない、または終了した勝利状態に移行している場合は、状態を失うと呼ばれるこの条件は次のことを意味します。この状態で運転されて誰が、誰が失うことになります

負け状態に移行することが可能な状態に対応する状態の勝利です

F [s]は、ゲームの状態の代表が勝つために、または失われます

fは本[S [i]は] = falseの場合、すべてのF [秒間[I] = trueの場合、F [S]が偽IF = [S]が真= F、あります

そして、何でしょうか?現在では、チームの残りの手の最後のカット。F [i] [j]は、現在の残りの秒を表しI、保存対戦相手がjに勝つか、失うケースです

列挙1 <= R <= k個* jを、[IR] [R] Fに転送することができます

メモリ検索

書式#include <iostreamの> 使用して名前空間はstdを、BOOL F [] []、G [] []。BOOL DFS(int型、IをINT J)
{ 場合(I == 0を返すもし(G [I] [J])戻り[I] [J] F。
    G [i] [j]は = 、[I] [j]は= F INT R = 1 ; R <= I && R <= k個* jを; R ++ 場合(DFS(IR、R)== )[i] [j]は= F 戻る[I] [J] F;

 




      
}

 int型のmain()
{ 
    CIN >> S >> K。
    INT A = 1 ++; <S であれば(DFS(SA、A)== 
        { 
            COUT << " アリス" << ENDL。
            リターン 0 ; 
        } 
    COUT << " ボブ" << ENDL。
    
    リターン 0 ; 
}

 

 

今、n個のゲームがどのように行う、がありますか?

例:小石ゲームを取ります

N-石が重ね、各石のパイル内部石の任意の数から除去することができます。石誰もが迷子になる方法はありませんとき。または勝つために、上側の手を失いますか?

 

石の唯一の山がある場合

SG機能

定義されたSGは[0] 0 SGを= [I]!= 0は、私は[i]は= 0 iが負けている状態を表して勝利SGのある状態を表しています

SG [1]:すべての状態はありませんが行われている最小の自然数を見つけ、その機能のSGがダウンして書かれ、1に転送することができます

SG [n]のために、最小のないSG [0]、SG [1] ... SGを見つけていない[N-1]は、天然に発生数が、この数はSG [n]の値です。

SG機能の使用は何ですか?

この問題に戻り、n個になるためにどのように、我々は唯一のゲームを見つけるために、SG機能を見つけることが、問題はゲームよりもですか?

SG定理:SGゲーム値Nが最大各ゲームSGのXORの値に等しいです。

証明:最初のカードを取ります

 

問題はその後、発見SG [n]は= nまでプッシュするので、直接、うまくやります

 

int main()
{
    cin >> n;
    int ans=0;
    for (int a=1;a<=n;a++)
    {
        int v;
        cin >> v;
        ans = ans ^v;
    }
    if (ans!=0) cout << "Alice" << endl;
    else cout << "Bob" << endl;
}

 

 

例:给你n堆石子,每一次可以在某一堆石子中取走1~4个石子,问先手必胜还是必败

列出来sg函数找找规律,发现sg[i]=i%5,然后抑或起来就好了

 

一般这种题就是自己手算出sg函数然后抑或起来就好了

 

 

例:n+1堆石子,最左边一堆石头有2012个,两个人分别进行操作。一次操作可以选取两堆不同的石堆分别增加或减少一个石子(一加一减,或给已经不剩石子的堆加一个都是允许的)。为了保证游戏会在有限步内结束,规定所选的两堆中右边的那一堆一定要包含奇数个石子,无路可走者输.问先手是否必胜?

 

这个题非常恶心的一点就是不同的堆有联系,所以考虑把他转化成最基本的问题

先看每一堆是奇数个还是偶数个,答案就是把所有奇数的下标取出来异或,看是否等于0

有N堆石子放在N级楼梯上,楼梯编号为0..N-1,每堆有a[n]个石子。两人轮流游戏,每次将任意堆中的任意个石子移动到它下面那一层楼梯上,0号的石子不能动。直到所有石子都移动到0号,那个人就赢了。问先手是否有必胜策略 。

 

把所有下标为奇数的石子数量异或起来

因为如果把偶数位置的石子扔到奇数上,那么我只需要把奇数的位置移到下一个偶数就好了

所以偶数位置上的石子对答案没有影响,且奇数位置上的石子搬到偶数上后就没有影响了

例:N*M的棋盘,每个格子有一定数量棋子,每次可将某个格子部分或全部棋子向右或向下移动,问先手必胜还是必败。

 

和上个题差不多,只需要把所有距离为奇数的点的距离异或起来就行了

因为如果从偶数移动到奇数,一定可以从奇数移动到偶数,反之则不然

 

例:1xN(1<=N<=2000)的空格子,双方轮流操作,每次选一个没有被标记的格子,将其标记,如果某人操作完后,存在3个连续的格子都被标记了,那么他就获胜了,问先手是否有必胜策略?

 

如果把他当成一个游戏,我们需要搞一个2000位的二进制数,存都存不下

考虑把他当成多个游戏

如果我们先手在一个位置打标记,后手就不能在这个位置左两个右两个打标记

状态sg[i]表示对于一个长度为i的横条它的sg是多少

Vector存所有能转移到的sg值

枚举染色点,把原来的横条分成两个小的横条,算出来左边和右边的长度

怎么计算mex?

先排序,从0,1,2,3这样一直看,直到找到一个没有的。为了避免数组越界,直接加一个很大的数就可以了

#include<iostream>
#include<algorithm>
#include<vector>

using namespace std;

int dfs(int n)
{
    if (n==0) return 0;
    if (f[n]) return sg[n];
    f[n]=true;
    vector<int> z;
    for (int a=1;a<=n;a++)
    {
        int l = max(a-3,0);
        int r = max(n-a-2,0);
        z.push_back( dfs(l) ^ dfs(r) );
    }
    sort(z.begin(),z.end());
    z.push_back(233333333);
    
    for (int a=0,p=0;;a++)
    {
        if (z[p] != a)
        {
            sg[n] = a;
            return sg[n];
        }
        while (z[p]==a)
            p++;
    }
}

int main()
{
    cin >> n;
    if (dfs(n)) cout << "Alice" << endl;
    else cout << "Bob" << endl;
}

 

看看能不能有一种情况转移到必败态

但是开30000*30000的数组就炸了

怎么优化

把状态改成f[a][b][y],因为x只可能是2的倍数或者3的倍数

 

おすすめ

転載: www.cnblogs.com/lcezych/p/11206638.html