您的位置:首页 > 其它

51Nod-1693-水群

2017-08-11 16:48 274 查看
ACM模版

描述



题解

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

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

现在我们说一下为什么这么做,首先,我们可以将 i 与 i−1 连边理解为操作三,也就是删除一个表情,然后将 i 连线 i∗k 理解为复制一次,粘贴多次,之所以只考虑这两种情况是因为,多次连续复制无意义,但是复制后多次粘贴是有意义的。那么我们为什么要将 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
;
}

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

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

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  最短路