51Nod 1693 水群(打表确定转移范围优化)

来源:互联网 发布:淘宝手机充值利润 编辑:程序博客网 时间:2024/06/05 19:28

1693 水群
基准时间限制:0.4 秒 空间限制:524288 KB 分值: 160 难度:6级算法题 收藏 关注
总所周知,水群是一件很浪费时间的事,但是其实在水群这件事中,也可以找到一些有意思的东西。
比如现在,bx2k就在研究怎样水表情的问题。
首先,bx2k在对话框中输入了一个表情,接下来,他可以进行三种操作。
第一种,是全选复制,把所有表情全选然后复制到剪贴板中。
第二种,是粘贴,把剪贴板中的表情粘贴到对话框中。
第三种,是退格,把对话框中的最后一个表情删去。
假设当前对话框中的表情数是num0,剪贴板中的表情数是num1,
那么第一种操作就是num1=num0
第二种操作就是num0+=num1
第三种操作就是num0–
现在bx2k想知道,如果要得到n(1<=n<=10^6)个表情,最少需要几次操作。
请你设计一个程序帮助bx2k水群吧。
Input
一个整数n表示需要得到的表情数
Output
一个整数ans表示最少需要的操作数
Input示例
233
Output示例
17
QwX (题目提供者)

解题思路:

  首先我们可以把复制和粘贴合并成一个操作,变成长度增加k倍。这样问题就变成了一个最短路问题,每个结点到编号是它k倍的边的长度为k,到比他编号减一的边长度为1,1到N的距离即为答案。
  但是这样时间复杂度很高,回忆矩阵快速幂的过程,实际上我们不需要连接任意倍数,只需要连接倍数是素数的边即可。这样写完打表输出一下中间变量可以发现只用到了2,3,5,7,11,13这几个素数。这样就可以通过这道题了。
   我们还可以继续优化,同样通过打表,我们可以发现连续的减一不超过5个,这样我们就可以使用记忆化搜索来限定减一的次数进行转移。

AC代码:

#include <iostream>#include <algorithm>#include <cstdio>#include <cstring>#define INF 0x3f3f3f3fusing namespace std;const int MAXN=1000000+3;int prime[6]={2, 3, 5, 7, 11, 13};int N, dp[MAXN][2];int dfs(int x,int flag)//flag==0表示只能有除法得到,flag==1表示可以由除法和减法得到{    if(x==1)        return 0;    if(dp[x][flag])        return dp[x][flag];    dp[x][flag]=INF;    for(int i=0;i<6;++i)//枚举倍数        if(x%prime[i]==0)            dp[x][flag]=min(dp[x][flag], dfs(x/prime[i], 1)+prime[i]);    if(!flag)        return dp[x][flag];    dp[x][0]=dp[x][1];    for(int i=1;i<6;++i)//枚举减一的个数        dp[x][1]=min(dp[x][1], dfs(x+i, 0)+i);    return dp[x][1];}int main(){    scanf("%d",&N);    printf("%d\n",dfs(N,1));    return 0;}
原创粉丝点击