传送门:点击打开链接
题意:刚开始只有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;
}