这波黑windows黑得很好我给满分,题目链接:HDOJ5802
看到两个数字的题,而且p,q都很大,不超过1e9,很明显数学题,先找规律,要么是公式,要么是打表,要么是贪心之类的算法
题意:p和q两个数,要求用最小步数从p走到q
如果选择减少,那么第一个单位时间减少1,如果前一个单位时间减少的是x,那么该单位时间减少2*x(相当于连击)
如果选择增加,那么每个单位时间增加1
如果选择休息,那么没有变化、
增加和休息对于减少来说都是打破连击
这样读完题,有一个很坑的边界条件,估计是比赛的时候很多队伍没有注意到的细节:
最大值无限,最小值不小于0
那么意味着什么呢?
100 95和6 1这两组数据的答案不一样!虽然都是差值都是5,但是走的路径是不一样的
100-99-97-93-94-95:需要5步
6-5-3-0-1:需要4步
因为最多到0,所以当减不动了的时候,直接跳到0
贪心的思路其实很明显:先判断p和q的大小,如果q不小于p,那么答案是q-p,因为只能一步一步往上爬
要么就是一直不断的减,减过分(减到不超过为止),然后再补回来,就是100 95的走法
要么就是减着减着停一下,减着减着加一下
减的次数其实是确定的,为int(log2(End-Start+1)),因为不能超过呗
细节有两个:
关注到0的细节,那么在判断的时候需要和0作判断
关注的停和加的细节,如果可以加,那么优先作加(因为如果停,最后再加,是浪费一步),需要从起点p到终点q,当前走了多少步,停了多少下作记录
代码如下:
int t;
__int64 q,p;
__int64 powtwo[1000];
__int64 dfs(__int64 S,__int64 T,__int64 step,__int64 stop){
if (S==T) return step;
__int64 x=S-T;
int two=(int)log2(x+1);
if (S-powtwo[two]+1==T) return step+two;
two++;
__int64 up=T-max((long long)0,S-powtwo[two]+1);
__int64 tmp=two+max((long long)0,up-stop);
return min(tmp+step,dfs(S-powtwo[two-1]+1,T,step+two,stop+1));
}
int main(){
//freopen("input.txt","r",stdin);
memset(powtwo,0,sizeof(powtwo));
powtwo[0]=1;
for(int i=1;;i++){
powtwo[i]=2*powtwo[i-1];
if (powtwo[i]>1e9) break;
}
scanf("%d",&t);
for(int Case=1;Case<=t;Case++){
scanf("%I64d%I64d",&p,&q);
if (q>=p) cout<<q-p<<endl;
else cout<<dfs(p,q,0,0)<<endl;
}
return 0;
}
有个类似的题:codeforces #356 div2 D
这个题也是类似的思路,因为数据量很大,也是几个数字,每次需要删除尽可能大的
如何制定正确的贪心策略?
其实这个题不要看m最多是1e15,其实立方数就1e6个,然后因为证明的数学规律给剪枝了很大一部分,每一步骤的选择,其实最多就是两种方案,所以,可以直接像贴的题解一样暴力搞好
贴个泰神的题解膜拜一发:泰神题解