ACM模版

描述

题解

这个题真的很神很神的……

我先说一下这个题怎么解:这个题可以转化为图论,将 i i1 连边,非要为 1 ,再将 i ik 连边,费用为 k ,然后跑一遍最短路。但是因为边数略多,我们需要优化一下,边数多主要是因为 ik k 比较多,我们需要将无用的 k 省去。这里我们直接将 k 限定为质数,并且进一步优化是这个 k 不会太大,只要 2,3,5,7,11,13 。最短路算法很多,用 spfa 会稍微快一些。

现在我们说一下为什么这么做,首先,我们可以将 i i1 连边理解为操作三,也就是删除一个表情,然后将 i 连线 ik 理解为复制一次,粘贴多次,之所以只考虑这两种情况是因为,多次连续复制无意义,但是复制后多次粘贴是有意义的。那么我们为什么要将 k 限制为质数呢?这个其实挺容易理解的,因为如果 k 是合数,他完全可以由多次代价低的操作组合在一起实现等价的效果,代价总和还是一样的。但是进一步优化就不是那么好理解了,大致的说就是,如果我们将一次复制进行过多次粘贴,他的效果不会比等同代价的更多组复制粘贴组合的效益高,所以我们无需考虑过大的质数情况。没有具体的证明,但是大致上应该是可以理解为什么的,如果不知道控制在多少比较合适,可以自己多卡几组数据试试。另外好像还有一个优化就是连续删除的操作最优不会超过四次,不要问我为什么,这个我也不是特别清楚。大概还是和质数的一些性质有关系吧……

代码

#include <queue>
#include <cstdio>
#include <cstring>

using namespace std;

const int MAXN = 1e6 + 10;
const int MAGIC = 5;

int n;
int dis[MAXN];
int vis[MAXN];
int prime[] = {
  6, 2, 3, 5, 7, 11, 13};

int spfa_bfs(int n)
{
    queue<int> q;
    memset(dis, 0x3f, sizeof(dis));
    memset(vis, 0, sizeof(vis));

    dis[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 <= prime[0] && x * prime[j] < n + MAGIC; j++)
        {
            int y = x * prime[j], cost = prime[j];
            if (dis[x] + cost < dis[y])
            {
                dis[y] = dis[x] + cost;
                if (!vis[y])
                {
                    vis[y] = 1;
                    q.push(y);
                }
            }
        }
        int y = x - 1, cost = 1;
        if (dis[x] + cost < dis[y])
        {
            dis[y] = dis[x] + cost;
            if (!vis[y])
            {
                vis[y] = 1;
                q.push(y);
            }
        }
    }

    return dis[n];
}

int main()
{
    scanf("%d", &n);

    printf("%d\n", spfa_bfs(n));

    return 0;
}