传送门:点击打开链接
题意:刚开始只有1个表情,现在有3种操作。操作1.复制,操作2.粘贴,操作3.退格
问要到恰好n个表情,需要的最少的操作数。
思路:这题的思路非常神
首先,我们考虑到把这道题转换成图论,i与i-1之间连一条边,费用为1,i与i*k之间连一条边,费用为k,然后跑一遍最短路。
但是,这里的边数太大了,这里就出现了我们第一个优化,这个优化我觉得值得我们思考。。
在连接i与i*k的边时,只连接k为质数时的点。很容易发现这样是正确的
这也给我们打开了思路,以后遇到乘法的,可以通过质数拆开。
然后是第二个优化,竟然最后k只用到了2,3,5,7,11,13这几个质数。所以被卡题时可以去尝试,看能不能再简化一下数据
然后是最最后一个,,竟然spfa比dijkstra快!我现在终于信spfa了(Orz
#include <map> #include <set> #include <cmath> #include <ctime> #include <stack> #include <queue> #include <cstdio> #include <cctype> #include <string> #include <vector> #include <cstring> #include <iomanip> #include <iostream> #include <algorithm> #include <functional> #define fuck(x) cout<<"["<<x<<"]" #define FIN freopen("input.txt","r",stdin) #define FOUT freopen("output.txt","w+",stdout) using namespace std; typedef long long LL; typedef pair<int, int> PII; const int MX = 1e6 + 10; const int INF = 0x3f3f3f3f; int d[MX], vis[MX], n; int prime[] = {0, 2, 3, 5, 7, 11, 13}, prear = 6; int spfa_bfs(int n) { queue <int> q; for(int i = 1; i <= n + 10; i++) { d[i] = INF; vis[i] = 0; } d[1] = 0; vis[1] = 1; q.push(1); while(!q.empty()) { int x = q.front(); q.pop(); vis[x] = 0; for(int j = 1; j <= prear && x * prime[j] < n + 10; j++) { int y = x * prime[j], cost = prime[j]; if( d[x] + cost < d[y]) { d[y] = d[x] + cost; if(!vis[y]) { vis[y] = 1; q.push(y); } } } int y = x - 1, cost = 1; if( d[x] + cost < d[y]) { d[y] = d[x] + cost; if(!vis[y]) { vis[y] = 1; q.push(y); } } } return d[n]; } int main() { //FIN; scanf("%d", &n); printf("%d\n", spfa_bfs(n)); return 0; }