P3210 [HNOI2010] 石取りゲーム

ゲーム理論に関する興味深い問題です!

しかし、まだ完全には理解できていません...

主なアイデアは「上三角」のケースを除外することです。このケースは見た目に劣っているように見えるため、プレイヤーは最後に選ぶ可能性が高く、**必ず連続して選択されます。つまり、先手が最初と三番目を、後手が二番目を選択します**。(理性的な理解は失敗し、感覚的に理解するしかありません)

「上三角」のケースを処理した後、大きいものから順に選択できます。

しかし、サンプルがすべてWA(Wrong Answer)になることに気づきます。これは境界に大きな値がある場合があり、その場合偶数個なら最後に「強制的に」選択され、奇数個なら最も内側のものは影響を受けないためです。

注目すべきは、両側に「下三角」のケースが現れる可能性があることです。この場合、まず「下向き」の部分を取り除く必要があります。

スタックを使用したコード:

signed main() {
    read(n);
    for (register int i = 1; i <= n; ++i) {
        read(stone_value[i]);
        is_selected[i] = stone_value[i] > 0;
        if (is_selected[i])    ++total_selected;
        total_sum += stone_value[i];
    }
    bool flag = false;
    for (register  int i = 1; i <= n; ++i) {
        if (!is_selected[i]) {
            if (!flag) {
                flag = true;
                memcpy(temp_stack, stack_data, sizeof(stack_data));
                memset(stack_data, 0, sizeof(stack_data));
                stack_top = temp_stack_top; temp_stack_top = 0;
            } else {
                while (temp_stack_top)    heap[++heap_total] = stack_data[temp_stack_top], temp_stack_top--;
            }
            continue;
        }
        stack_data[++temp_stack_top] = stone_value[i];
        while (temp_stack_top > 2 && stack_data[temp_stack_top - 1] >= stack_data[temp_stack_top] && 
               stack_data[temp_stack_top - 1] >= stack_data[temp_stack_top - 2])
            --temp_stack_top, stack_data[temp_stack_top] = stack_data[temp_stack_top + 1] + stack_data[temp_stack_top - 1] - stack_data[temp_stack_top], 
            --temp_stack_top, stack_data[temp_stack_top] = stack_data[temp_stack_top + 1];
    }
    
    
    while (stack_top > 1 && temp_stack[stack_top] >= temp_stack[stack_top - 1])    heap[++heap_total] = temp_stack[stack_top], --stack_top;
    if (stack_top <= 1) {
        while (stack_top)    heap[++heap_total] = temp_stack[stack_top], stack_top--;
    } else {
        for (register int i = 1; i + 1 <= stack_top; i += 2) {
            difference += (total_selected & 1 ? 1 : -1) * (temp_stack[i] - temp_stack[i + 1]);
        }
        if (stack_top & 1)    heap[++heap_total] = temp_stack[stack_top];
    }
    int start = 1;
    while (start < temp_stack_top && stack_data[start] >= stack_data[start + 1])    heap[++heap_total] = stack_data[start], ++start;
    if (temp_stack_top - start + 1 <= 1) {
        while (temp_stack_top >= start)    heap[++heap_total] = stack_data[temp_stack_top], --temp_stack_top;
    } else {
        for (register int i = temp_stack_top; i - 1 >= start; i -= 2) {
            difference += (total_selected & 1 ? 1 : -1) * (stack_data[i] - stack_data[i - 1]);//注意!!!!!
        }
        if ((temp_stack_top - start + 1) & 1)    heap[++heap_total] = stack_data[start];
    }
    sort(heap + 1, heap + 1 + heap_total);
    int toggle = 1;
    for (register int i = heap_total; i; --i) {
        difference += toggle * heap[i];
        toggle *= -1;
    }
    printf("%lld %lld\n", (difference + total_sum) / 2, (total_sum - difference) / 2);
    return 0;
}

双方向リストを使用した簡略化版コード:

int main()
{
    read(n), right[0]=1, left[n+1]=n;
    for(int i=1;i<=n;++i)
        read(stone_value[i]), total_sum+=stone_value[i], left[i]=i-1, right[i]=i+1, is_valid[i]=(stone_value[i]!=0);
    for(int i=3;i<=n;i=right[i])
        while(is_valid[left[left[i]]] && is_valid[left[i]] && is_valid[i] && 
              stone_value[left[i]]>=stone_value[left[left[i]]] && stone_value[left[i]]>=stone_value[i])
            stone_value[i]=stone_value[left[left[i]]]+stone_value[i]-stone_value[left[i]], 
            right[left[left[left[i]]]]=i, left[i]=left[left[left[i]]];
    L=right[0], R=left[n+1];
    while(stone_value[L]>=stone_value[right[L]] && is_valid[L] && is_valid[right[L]]) 
        bonus+=stone_value[right[L]]-stone_value[L], L=right[right[L]];
    while(stone_value[R]>=stone_value[left[R]] && is_valid[R] && is_valid[left[R]]) 
        bonus+=stone_value[left[R]]-stone_value[R], R=left[left[R]];
    for(int i=L;i<=R;i=right[i])
        if(is_valid[i])
            stone_value[++total_selected]=stone_value[i];
    sort(stone_value+1,stone_value+total_selected+1,compare), stone_value[++total_selected]=bonus;
    for(int i=1;i<=total_selected;++i)
    {
        if(i&1) result_value+=stone_value[i];
        else result_value-=stone_value[i];
    }
    printf("%lld %lld",(total_sum+result_value)/2,(total_sum-result_value)/2);
    return 0;
}

タグ: ゲーム理論 競技プログラミング 石取りゲーム スタック 双方向リスト

7月15日 16:01 投稿