老虎ji 剪枝模擬

2022-05-01 17:03:09 字數 2242 閱讀 7527

「在賭場裡,基本原則就是讓他們玩下去以及讓他們再來玩。他們玩得越久,他們會輸的越多,最後,我們會得到一切。」

(摘自2023年的電影casino)

你正在一家賭場的老虎ji面前,你的手上共有k

個遊戲幣。

當你每次按下老虎ji的按鈕時,它會隨機地報出乙個數字d

,如果d=0

,那麼什麼事情都不會發生;如果d>0

,那麼恭喜你,老虎ji會吐給你d

個遊戲幣;但是如果d<0

,很不幸,你將需要支付給老虎ji d

個遊戲幣。

這台老虎ji的工作原理很簡單,在老虎ji內部有乙個長度為n

的序列a[0],a[1],…,a[n−1]

,每當你按下按鈕時,如果你手上共有k

個遊戲幣,那麼它報出的數字d

就是a[kmodn]

。前幾次賭博讓你嘗到了甜頭,貪婪的慾望驅動著你不停地按下老虎ji的按鈕。當d<0

且你支付不了那麼多遊戲幣時,你宣告破產。請問在你破產的時候你一共按過多少次按鈕呢?

第一行包含乙個正整數t(1≤t≤5000)

,表示測試資料的組數。

每組資料第一行包含兩個正整數n,k(1≤n≤100000,1≤k≤1018)

,表示序列長度以及你初始的遊戲幣數量。

第二行包含n

個整數a[0],a[1],…,a[n−1](−109≤a[i]≤109)

。輸入資料保證所有資料中n

的總和不超過1000000

。對於每組資料輸出一行乙個整數,即你破產的時候你一共按過按鈕的次數。如果你運氣很好,永遠都不會破產,請輸出−1

1

5 20

-1 -2 3 4 -5

5

顯然最普通的模擬方法肯定會超時

用另一種模擬方法 時間複雜度n

模擬第一輪 直到遇到迴圈 也就是第二次走到某個地方

然後就可以求出 一遍迴圈走的步數c 和一遍迴圈減小的數值d

顯然 如果d<=0那麼就是死迴圈 輸出-1即可

一開始:

當前k除 n*d 到k為負數

但是錯誤性很明顯:不可能每次都是迴圈結束才-k 很可能在迴圈的中間就已經減到負數了 所以cnt會偏大

調整:預留一次迴圈:就是把k變為k%d(之前的想法是把k變成負數) 預留最後一次迴圈自己模擬 正確性也很明顯

但是還有個bug

可能每次迴圈的差值d很小 但是在迴圈過程中 起伏非常大 在倒數第二遍或者。。。就已經被減為負數了

所以再調整:

求出d的最大連續和(我這裡求的是極限數值) 讓k比d大的時候開始最原始模擬

#includeusing

namespace

std;

//input

#define rep(i,a,b) for(int i=(a);i<=(b);i++)

#define repp(i,a,b) for(int i=(a);i>=(b);i--)

#define ri(n) scanf("%d",&(n))

#define rii(n,m) scanf("%d%d",&n,&m);

#define riii(n,m,k) scanf("%d%d%d",&n,&m,&k)

#define rs(s) scanf("%s",s);

#define ll long long

#define inf 0x3f3f3f3f

#define rep(i,n) for(int i=0;i

#define clr(a,v) memset(a,v,sizeof a)

/////////////////////////////////

/#define n 100000+10ll a[n];

ll vis[n][2];

intmain()

if(!vis[pos][0

])

else

maxx=-maxx;

ll h=k/d;

cnt+=h*c;

k%=d;

while(k<=maxx)//

回補

while(1

)

}break

; }}}

}

view code