最短路 51Nod1693 水群

传送门:点击打开链接

题意:刚开始只有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;
}


你可能感兴趣的:(最短路 51Nod1693 水群)