1. 程式人生 > >Lyft Level 5 Challenge 2018 C. Permutation Game

Lyft Level 5 Challenge 2018 C. Permutation Game

必勝點和必敗點的概念: P點:必敗點,換而言之,就是誰處於此位置,則在雙方操作正確的情況下必敗。 N點:必勝點,處於此情況下,雙方操作均正確的情況下必勝。 必勝點和必敗點的性質: 1、所有終結點是 必敗點 P 。(我們以此為基本前提進行推理,換句話說,我們以此為假設) 2、從任何必勝點N 操作,至少有一種方式可以進入必敗點 P。 3、無論如何操作,必敗點P 都只能進入 必勝點 N。

這道題,兩種做法: 第一種做法:拓撲+博弈 由於是一個排列,那麼可以列舉每個數,判斷這個位置的a是否大於它,如果可以連邊。這樣的複雜度是nlogn的。。。 然後對於一些無路可走的點,這是必敗態,根據必勝和必敗的定義:必勝態的後繼狀態中存在至少一個必敗態,必敗態的後繼狀態全是必勝態。 然後建反圖,在DAG上拓撲。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int inf = 0x3f3f3f3f;
const double PI = acos(-1);
const double eps = 1e-8;
#define pb push_back
#define mp make_pair
#define fi first
#define se second
const int N = 1e5 + 5;

int a[N];
vector<int>ve[N];
int deg[N];
int ans[N];

int main()
{
    int n;
    scanf("%d",&n);
    for(int i = 1;i <=  n;++i){
        scanf("%d",&a[i]);
    }
    memset(deg,0,sizeof(deg));
    memset(ans,-1,sizeof(ans));
    for(int i = 1;i <= n;++i){
        for(int j = i;j <= n;j += a[i]){
            if(a[j] > a[i]) ve[j].pb(i),deg[i]++;
        }
        for(int j = i;j >= 1;j -= a[i]){
            if(a[j] > a[i]) ve[j].pb(i),deg[i]++;
        }
    }
    queue<int>que;
    for(int i = 1;i <= n;++i){
        if(!deg[i]) que.push(i),ans[i] = 0;
    }
    while(!que.empty())
    {
        int u = que.front();
        que.pop();
        int len = ve[u].size();
        for(int i = 0;i < len;++i){
            int v = ve[u][i];
            if(ans[v] == -1){
                if(ans[u] == 0) ans[v] = 1;
                else ans[v] = 0;
            }else{
                if(ans[u] == 0) ans[v] = 1;
            }
            deg[v]--;
            if(!deg[v]) que.push(v);
        }
    }
    for(int i = 1;i <= n;++i){
        if(ans[i]) printf("A");
        else printf("B");
    }
    printf("\n");
    return 0;
}

第二種做法: 逆向處理n…1;算ans[i]的時候,i + 1 …n的ans值已經算出,與i相關的值必須是比i大的,那麼找i 的後繼狀態,存在一個必敗態,則ans[i]為必勝態,如果找到的都是必勝態的,那麼ans[i]為必敗態

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int inf = 0x3f3f3f3f;
const double PI = acos(-1);
const double eps = 1e-8;
#define pb push_back
#define mp make_pair
#define fi first
#define se second
const int N = 1e5 + 5;

int a[N];
int b[N];
int ans[N];

int main()
{
    int n;
    scanf("%d",&n);
    for(int i = 1;i <= n;++i){
        scanf("%d",&a[i]);
        b[a[i]] = i;
        //初始化為必敗態
        ans[i] = 0;
    }
    //倒著遍歷,是每個i找到的j的ans值是已經求出來了
    for(int i = n;i >= 1;--i){
        //找到i的下標
        int x = b[i];
        //x % i是為了找到第一個滿足(j - x) % i == 0的下標
        for(int j = x % i;j <= n;j += i){
            //只要後繼狀態存在一個必敗態,那麼當前必是必勝態
            if(a[j] > a[x]) ans[x] |= (!ans[j]);
        }
    }
    for(int i = 1;i <= n;++i){
        if(ans[i])
            printf("A");
        else
            printf("B");
    }
    printf("\n");
    return 0;
}