• 51nod1693 水群


    QwX (题目提供者)
     
    首先简化题目,题面的意思就是,当前有一个数s
    操作1是s*=k代价为k,操作2是s--代价为1
    求把s从1变到n的最小代价
     
    做法1:
    直接暴力记忆化搜索,F(i)表示从1到i用的最小操作数
    则F(i)=min(F(i+1),min{F(k)+n/k|n%k==0})
    当然这样做肯定过不了
    做法2:
    考虑把问题转换成图论模型,对于每个i
    连边i→i-1边权为1,连边i→i*k边权为k
    那么题目就是要求点1到点n的最短路
    但是很显然这样复杂度还是很高并不能过
    做法3:
    注意到如果一条边i→i*k能用若干条边代替
    那么i→i*k这条边就是没有意义的
    因为那些边的权值的乘积等于k
    即那些边的权值和小于等于k
    因此对于每个i,只用连i→i*p的边,其中p是质数
    这样就可以让边数变得很小了
    但是0.1s还是过不去
    做法4:
    既然过不去,就可以想一些别的办法
    考虑在做法3中记录每一个点的最短路路径
    就是打一个每个点自己最短路上的上一个点的表
    那么可以从表中发现,i→i-1的边不会连续出现4次以上
    而且i→i*p的边只有当p<=13的时候才有意义
    于是又可以删掉很多边,边数大概是3*10^6条
    但是这样做复杂度还是不够,用时稍微多于0.1s
    做法5:
    既然最短路的做法复杂度太高,就可以再换个思路。
    重新考虑记忆化搜索的做法,F(i,j)表示从1到i上一条边的状态是j的最少操作数
    j=0时上一条边可以是i→i-1的,否则就是i→i*p的边
    那么F(i,0)=min(min{F(i+k,1)|0<k<5},min{F(i/p,0)|p<13})
    于是这样做就可以把时间降到最低,从而做出此题
     
    不忘初心 方得始终
     
    PS:后来因为时限问题把时限改成了0.4s,所以做法3就A掉这个题
    而且经过我自己的测试,做法5是可以在1s之内通过10^9的数据的
    如果大家有兴趣可以试着打一下,毕竟也是一个很好的思路
     
    我用了做法4。做法5看不懂。。。做法4一开始我加边部分点tle了,不加边就A了。
    #include<cstdio>
    #include<cstring>
    #include<cctype>
    #include<algorithm>
    #include<queue>
    using namespace std;
    #define rep(i,s,t) for(int i=s;i<=t;i++)
    #define dwn(i,s,t) for(int i=s;i>=t;i--)
    #define clr(x,c) memset(x,c,sizeof(x)) 
    #define qwq(x) for(edge *o=head[x];o;o=o->next)
    const int nmax=2e6+5;
    const int maxn=1e7+5;
    const int inf=0x7f7f7f7f;
    struct node{
    	int x,dist;
    	node(int x,int dist):x(x),dist(dist){};
    	node(){};
    	bool operator<(const node&rhs)const{
    	  return dist>rhs.dist;}
    };
    priority_queue<node>q;
    int dist[nmax];
    const int a[]={2,3,5,7,9,11,13};
    int dij(int x){
    	clr(dist,0x7f);dist[1]=0;q.push(node(1,0));
    	node o;int tx,td,to;
    	while(!q.empty()){
    		o=q.top();q.pop();tx=o.x;td=o.dist;
    		if(dist[tx]!=td) continue;
    		rep(i,0,5){
    			to=tx*a[i];
    			if(to<x+10&&dist[to]>td+a[i]){
    				dist[to]=td+a[i];q.push(node(to,td+a[i]));
    			}
    		}
    		if(tx&&dist[tx-1]>dist[tx]+1){
    			dist[tx-1]=dist[tx]+1;q.push(node(tx-1,dist[tx-1]));
    		}
    	}	 
    	return dist[x];
    }
    int main(){
    	int n;scanf("%d",&n);
    	printf("%d
    ",dij(n));
    	return 0;
    }
    

      

    基准时间限制: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
  • 相关阅读:
    堆排序算法
    归并排序的递归算法与非递归
    二叉排序树(BST)的建立
    枚举排列的两种常见方法
    UVa 439骑士的移动(BFS)
    UVa 二叉树重建(先序+中序求后序)
    UVa 四叉树
    UVa 10562看图写树(二叉树遍历)
    JDBC(6)事务处理&批量处理
    JDBC(5)ResSetMetaData&DatabaseMetaData&获取数据库主键的值
  • 原文地址:https://www.cnblogs.com/fighting-to-the-end/p/5874763.html
Copyright © 2020-2023  润新知