1. 程式人生 > >TOJ4101.Guess Game(TOJ means Tianjin University Online Judge)(dp的思想,但這道題目是假dp)

TOJ4101.Guess Game(TOJ means Tianjin University Online Judge)(dp的思想,但這道題目是假dp)

line bre 個人 eof ret calculate code stdio.h turn

題意:你要從[1,n]這個n個數中猜出來規定的某個數,現在這個數未知,問你在最糟糕的情況下(但是你采用了最優的策略),你要猜多少次才能猜出這個數。現在有兩種條件:

第一種:當你猜的數比指定的那個數小的時候,系統會提示你small;

第二種:當你猜的數比指定的那個數大的時候,系統會提示你wrong,但是從這以後不論你猜的數比指定數大或小,系統將永遠提示你wrong。

在這裏最糟糕情況可以理解為這個人很倒黴,命運總是讓他多猜。

分析:

數列:1,2,3,4,....,k,....,n

比如說你猜了數字k,那麽現在有兩種情況:

α.你猜的數字比指定的數字大了,由於你很倒黴,所以你得一個個地往小裏猜,此時最糟糕的情況是指定數字是1,那麽你要猜k - 1次;

β.你猜的數字比指定的數字小了,那麽相當於又開始了一個規模為n-k的遊戲;

現在定義dp[i]---->在最糟糕的情況下,從規模為i的數列中猜數,最少要猜多少次。

那麽我們應該怎麽制定自己的策略呢?由於你很倒黴,那麽命運一定會讓你進入兩種情況中多的那一種。那麽好,我就讓兩種情況要猜的數盡量相等,這樣就是最優的策略。

dp[i] = min{max(k-1,dp[i-k])+1;

memset(dp,F,sizeof(dp));
    dp[0] = 0,dp[1] = 1,dp[2] = 2,dp[3] = 2;
    for(int i = 4;i <= 10000;++i){
        
for(int j = 1;j <= i;++j){ dp[i] = min(dp[i],max(dp[i - j],j - 1) + 1); } }

然而n<= 109,這樣的時間復雜度是O(n2),空間復雜度是O(n)。

即爆時間又爆空間。

這就是為啥說這是個假dp了,然而讓人驚喜的是,這道題目的得數有規律:

從1到n對應的得數分別是:1個1,2個2,3個3,4個4......

那這個就相當於解一個不等式了,(X2+X)/2 ≥ n,由於在定義域上單調,這裏用二分搜索即可:

AC代碼:

#include<stdio.h>
#include
<string.h> #include<iostream> #include<algorithm> //#define LOCAL using namespace std; int calculate(int x) { return (x * x + x)>>1; } int main() { //#ifdef LOCAL //freopen("TOJ4101.txt","r",stdin); //freopen("TOJ4101out.txt","w",stdout); //#endif int T; scanf("%d",&T); while(T--){ int n; scanf("%d",&n); int rht = 10000,lft = 1,mid = (lft + rht)>>1,temp; while(rht != lft){ temp = calculate(mid); if(temp < n) lft = mid + 1; else if(temp == n){ lft = mid; break; } else if(temp > n){ rht = mid; } mid = (lft + rht)>>1; //printf("left is %d and right is %d\n",lft,rht); } printf("%d\n",lft); } return 0; }

TOJ4101.Guess Game(TOJ means Tianjin University Online Judge)(dp的思想,但這道題目是假dp)