P3210 [HNOI2010]取石頭遊戲
阿新 • • 發佈:2020-08-05
博弈論好題!
但是還沒有完全弄懂...
主要的思想就是判掉“上三角”的情況,因為這種情況看起來比較劣,大家會把它放最後再選,並且一定會連續地選,即“先手”選第一個和第三個,“後手”選第二個.(理性理解失敗,只能感性理解)
處理掉“上三角”以後,就可以直接從大到小選了。
然後會發現樣例都 WA 了。這是因為邊界可能出現靠邊的位置比較大的情況,而這時如果有偶數個,那麼肯定留在最後“被迫”選擇;如果有奇數個,那麼最靠裡的那個不會受到影響。
值得注意的是,兩邊還可能出現“下三角”的情況,這時需要我們先拿走“向下”的那部分。
程式碼(棧):
signed main() { read(n); for (register int i = 1; i <= n; ++i) { read(v[i]); vis[i] = v[i] > 0; if (vis[i]) ++tot; sum += v[i]; } bool flag = false; for (register int i = 1; i <= n; ++i) { if (!vis[i]) { if (!flag) { flag = true; memcpy(tmp, stk, sizeof(stk)); memset(stk, 0, sizeof(stk)); top = stop; stop = 0; } else { while (stop) h[++htot] = stk[stop], stop--; } continue; } stk[++stop] = v[i]; while (stop > 2 && stk[stop - 1] >= stk[stop] && stk[stop - 1] >= stk[stop - 2]) --stop, stk[stop] = stk[stop + 1] + stk[stop - 1] - stk[stop], --stop, stk[stop] = stk[stop + 1]; } while (top > 1 && tmp[top] >= tmp[top - 1]) h[++htot] = tmp[top], --top; if (top <= 1) { while (top) h[++htot] = tmp[top], top--; } else { for (register int i = 1; i + 1 <= top; i += 2) { cha += (tot & 1 ? 1 : -1) * (tmp[i] - tmp[i + 1]); } if (top & 1) h[++htot] = tmp[top]; } int st = 1; while (st < stop && stk[st] >= stk[st + 1]) h[++htot] = stk[st], ++st; if (stop - st + 1 <= 1) { while (stop >= st) h[++htot] = stk[stop], --stop; } else { for (register int i = stop; i - 1 >= st; i -= 2) { cha += (tot & 1 ? 1 : -1) * (stk[i] - stk[i - 1]);//Attention!!!!! } if ((stop - st + 1) & 1) h[++htot] = stk[st]; } sort(h + 1, h + 1 + htot); int tp = 1; for (register int i = htot; i; --i) { cha += tp * h[i]; tp *= -1; } printf("%lld %lld\n", (cha + sum) / 2, (sum - cha) / 2); return 0; }
劉隊的簡化版程式碼(雙向連結串列):
int main() { read(n),r[0]=1,l[n+1]=n; for(int i=1;i<=n;++i) read(v[i]),sum+=v[i],l[i]=i-1,r[i]=i+1,tag[i]=(v[i]!=0); for(int i=3;i<=n;i=r[i]) while(tag[l[l[i]]]&&tag[l[i]]&&tag[i]&&v[l[i]]>=v[l[l[i]]]&&v[l[i]]>=v[i]) v[i]=v[l[l[i]]]+v[i]-v[l[i]],r[l[l[l[i]]]]=i,l[i]=l[l[l[i]]]; L=r[0],R=l[n+1]; while(v[L]>=v[r[L]]&&tag[L]&&tag[r[L]]) s+=v[r[L]]-v[L],L=r[r[L]]; while(v[R]>=v[l[R]]&&tag[R]&&tag[l[R]]) s+=v[l[R]]-v[R],R=l[l[R]]; for(int i=L;i<=R;i=r[i]) if(tag[i]) v[++tot]=v[i]; sort(v+1,v+tot+1,cmp),v[++tot]=s; for(int i=1;i<=tot;++i) { if(i&1) val+=v[i]; else val-=v[i]; } printf("%lld %lld",(sum+val)/2,(sum-val)/2); return 0; }