九省聯考2018 一雙木棋 chess

2022-02-04 18:39:13 字數 2738 閱讀 1653

菲菲和牛牛在一塊n 行m 列的棋盤上下棋,菲菲執黑棋先手,牛牛執白棋后手。 棋局開始時,棋盤上沒有任何棋子,兩人輪流在格仔上落子,直到填滿棋盤時結束。

落子的規則是:乙個格仔可以落子當且僅當這個格仔內沒有棋子且這個格仔的左側及上方的所有格仔內都有棋子。

棋盤的每個格仔上,都寫有兩個非負整數,從上到下第i 行中從左到右第j 列的格 子上的兩個整數記作ai,j,,bi,j

。在遊戲結束後,菲菲和牛牛會分別計算自己的得分:菲菲的得分是所有有黑棋的格仔上的ai,j

​之和,牛牛的得分是所有有白棋的格仔上的bi,j

的和。菲菲和牛牛都希望,自己的得分減去對方的得分得到的結果最大。現在他們想知道,在給定的棋盤上,如果雙方都採用最優策略且知道對方會採用最優策略,那麼,最終的結果如何。

輸入第一行包含兩個正整數n;m,保證n;m <= 10。

接下來n 行,每行m 個非負整數,按從上到下從左到右的順序描述每個格仔上的 第乙個非負整數:其中第i 行中第j 個數表示ai,j

。接下來n 行,每行m 個非負整數,按從上到下從左到右的順序描述每個格仔上的 第二個非負整數:其中第i 行中第j 個數表示bi,j

​。輸出乙個整數,表示菲菲的得分減去牛牛的得分的結果。

對於所有的測試資料,n;m <= 10 ,ai,j

; bi,j

​<= 100000。

對於編號為奇數的測試點,保證所有的bi,j

考場上沒想出來寫的 30 分暴力誒

沒想到現在就已經會了

我們定義某一時刻棋盤上的落子情況為當前的狀態

定義 s 為初狀態,即棋盤上還沒有落子

定義 t 為末狀態,即棋盤上已經落完子

不難證明,合法的狀態小於二十萬種

那麼先 hash 一下每個狀態,令其唯一對應乙個正整數

對於每乙個狀態,我們可以知道它是從哪些狀態轉移來的

定義 num[i] 表示 i 狀態落了多少子,方便判斷當前是該先手還是該後手。

我們 dp 要倒著推,因為如果正著推,有可能出現當前雖然求出了最大價值,但是卻不是他們的最優策略的情況。

所以定義 f[i] 表示從狀態 i 到末狀態 t 先手減後手的最大價值

f[t] 初值為0,f[1] 即為答案

但是怎麼求中間狀態 f[i] 的值呢?

之前提到過,可以求出 i 狀態是由 哪些狀態轉移來的,假設有乙個狀態為 j 可以轉移到 i

我們用 num 陣列求出在狀態 j 時是先手下了還是後手下了最後乙個棋子,然後分情況討論

如果是先手:考慮後手的最優策略,顯然是想讓 f[i] 最小,所以 f[i]=min,x、y 是 j 轉移到 i 狀態落子的橫縱座標

同理,如果為後手:那麼 f[i] 最大的轉移方程是 f[i]=max,x、y 的意義跟上面一樣

那我們現在就剩最後乙個問題了:怎麼進行轉移呢?

我這裡利用了拓撲序進行轉移:如果乙個狀態被所有的後續狀態遍歷完並求出最優解後,就將其 push 進佇列裡,讓它去轉移別人即可。

最壞情況時間複雜度 $o(18萬*180萬)$ 但是開氧氣優化跑的賊快,最慢的點 300ms (反正省選也開 o2 不算作弊)

//

by youngneal

#include#include

#include

#include

#define n 400005

#define int long long

#define mod 1000000007

using

namespace

std;

inthead[n];

intcnt,s,t;

intn,m,tot;

int qp[n][15

];int f[n],fz[15

];int

deg[n],num[n];

int a[15][15],b[15][15

];map

mp;queue

topo;

struct

edgeedge[n*10

];void add(int x,int y,int z,int

p)void hsh(int

x)void dfs(int now,int lim,int

num)

for(int i=0;i<=lim;i++)

fz[now]=i,dfs(now+1,i,num+i);

}void

_find()

void read(int &x)

signed main()

for(int i=1;i<=n;i++)

dfs(

1,m,0

); _find();

f[t]=0

;

for(int i=2;i<=tot;i++)

if(num[i]&1) add(i,mp[x],a[idx][idy],0

);

else add(i,mp[x],0

,b[idx][idy]);

deg[mp[x]]++;}}

topo.push(t);

while

(topo.size())

}printf(

"%lld\n

",f[1

]);

return0;

}

九省聯考2018 一雙木棋

我們容易知道,棋子的擺放形狀應該是乙個階梯性 某dalao 來來來,輪廓線dp!我們選擇用狀態壓縮來表示整個棋盤的排放形態 11進製壓位,每個位上的數都表示一行的狀態 這樣子我們每次對合法狀態進行拓展,dfs搜尋下去。輪到菲菲,我們期望她的得分大一些,而到牛牛,我們期望她的得分少一些。所以我們對前者...

九省聯考2018 一雙木棋chess

傳送門 這道題乍一看思路只有打個暴力。題目要求 乙個格仔可以落子當且僅當這個格仔內沒有棋子且這個格仔的左側及上方的所有格仔內都有棋子,即棋子一定都分布在左上角。也就是說塗了色的格仔的形狀一定是下面這樣的,我們考慮橫邊為1,豎邊為0,這個格仔的所有邊就是101010 沒包括其他邊界,只看藍色部分 考慮...

九省聯考2018 一雙木棋chess

據說這題是可以暴力踩過去的。還是考慮正解吧,是一種叫 輪廓線dp 的只聽過沒寫過的東西 不難發現,最後拿出來的棋子一定是左上角佔的一塊區域。發現 n m leq 20 我們可以狀壓一下這個區域右上到左下的邊界。具體來說,我們存乙個 2 以內的數,1 表示向下 0 表示向左。是否需要再開一維狀態維護當...