I.A Small Game
I. A Small Game
题解:
这一题可以反向思考,考虑如何从x得到0.因为本题的倍乘机制决定了即使我们用更小的代价到达了更大的x值,这个x值不一定会在最优路线中被用到.因此我们不能根据x的绝对大小及当前代价直接剪枝,如果反向递推时,当前值为偶数时的最优情况比较特殊,对于偶数,加1一定不是最优,如果减1,可以确定从x减到0才是最优,因此递归调用树的分支将大大减少,可以将搜索的时空消耗减少至可接受范围.
记f(x)为经过若干次操作,使x的值变为0的最小花费.则f(x)的递推公式为:
\[f(x)=
\begin{cases}
min\left(\,f\left(\frac{x}{2}\right)+b,x*a\right) &\left(\,x为偶数\,且x不为0\right)\\
min\left(\,f\left(\,x+1\right)+c,f\left(x-1\right)+a\right) &\left(\,x为奇数\,且x不为1\right)
\\
0 &\left(x=0\right)
\\
a &(x=1)
\end{cases}
\]
证明:
当x为奇数时,显然
当x为偶数时,不考虑加一操作
\[f \left(x \right)=min \left(f \left( \frac{x}{2}\right) + b,f(x-1) +a \right)
\]
而
\[ f \left(x-1\right)=min \left(f \left(x\right) + c ,f \left(x-2\right)+a \right)\\
\]
显然由\(f(x)\)推至\(f(x-1)\)时,\(f(x-1)\) 不等于 \(f(x)+c\)
所以
\[f \left(x \right)=min \left(f \left( \frac{x}{2}\right) + b,f(x-2) +2*a \right)
\]
同理可得
\[
f \left(x-2\right) = min \left( f \left( \frac{x-2}{2} \right) +b , f \left(x-4\right) + 2*a \right)
\]
代入得:
\[f \left(x\right)= min \left( f \left( \frac{x}{2} \right) +b \, ,
f \left( \frac{x}{2}-1 \right) +b+2*a\,,
f \left(x-4\right)+4*a
\right)
\]
\(f \left( \frac{x}{2} \right) +b \,\) 一定比 \(f \left( \frac{x}{2}-1 \right) +b+2*a\) 更优
因此
\[
f \left(x\right)=min \left( f \left( \frac{x}{2} \right) +b \, , f \left(x-4\right)+4*a \right)
\]
继续向下递推至0后可以得到:
\[
f \left(x\right)=min \left( f \left( \frac{x}{2} \right) +b \, , x*a \right)
\]
考虑加1操作时,采用类似方法可得先加1后必须再加1才取最优,而两次加一后无论是除2还是再减1都不是最优,只有继续加一可能取最优,为了得到最优值,x需要不断加一,这是很荒谬的,所以对于偶数,加1一定不是最优的。
代码:
#define _CRT_SECURE_NO_WARNINGS
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
map<ll,ll> m;
ll x,a,b,c,cnt=0;
ll f(ll x)
{
cnt++;
if(m[x])return m[x]-1;
if(x==0)return 0;
if(x==1)return a;
if(x%2==0)m[x]=min(f(x/2)+b,x*a)+1;
else m[x]=min(f(x-1)+a,f(x+1)+c)+1;
return m[x]-1;
}
int main()
{
while(~scanf("%lld %lld %lld %lld",&x,&a,&b,&c))
{
m.clear();
printf("%lld\n",f(x));
}
return 0;
}