2017 ICPC沈阳站 G题 Infinite Fraction Path【多源BFS+剪枝】

该博客主要介绍了使用优先队列优化的BFS算法解决一道ACM竞赛题目。题目要求找到一个特殊的数列,通过每次平方加一的操作,使得每一步操作后数列中最大值的路径。作者提出了两个剪枝条件来提高效率,最终实现了O(n*L)的时间复杂度,其中L为环的大小。博客内容涉及到图的遍历、优先队列和剪枝策略。
摘要由CSDN通过智能技术生成

题目链接

http://acm.hdu.edu.cn/showproblem.php?pid=6223

思路

用优先队列进行BFS,优先队列中记录当前点的层数、点的值、点的编号,按层数从小到大排序,若层数相同则按点值从大到小排序。

最开始把数组中所有权值等于最大值的点放入优先队列 (相当于有多个起点,即多源BFS), 然后开始逐层扩展,考虑两个剪枝:

  • 同层只取所有权值等于最大值的点。(小于最大值的直接跳过,不再扩展)
  • 同层若出现了多个相同编号的点,只留一个。(因为本题中每个点的后继是唯一的,那么多个相同编号的点只需要一个去扩展就行了)

AC代码

#include <bits/stdc++.h>
using namespace std;
const int N=150000+10;
typedef long long int;
int vis[N]; // 记录id上一次出现的层数
int T,n,mx,val[N],nxt[N],ans[N];
struct node
{
    int dep,val,id;
    bool operator < (const node &s) const
    {
        if(dep!=s.dep)return dep>s.dep; // 优先队列关系写相反的!!!
        return val<s.val;
    }
};
void bfs()
{
    priority_queue<node>q;
    memset(vis,-1,sizeof(vis));
    for(int i=0;i<n;i++)
        if(val[i]==mx)q.push({0,val[i],i}); // 第0层,取整个数组中的多个最大值
    int last_dep=-1; // 记录已经扩展到的层号
    while(!q.empty())
    {
        node tmp=q.top();q.pop();
        int id=tmp.id;
        int dep=tmp.dep;
        int v=val[id];
        if(dep>last_dep) // 扩展到了新的一层,第一个就是当前层的最大值
        {
            mx=v; // 当前层的最大值
            ans[dep]=mx;
            if(dep==n-1)return;
            last_dep=dep;
        }
        if(v<mx||vis[id]==dep)continue; // 剪枝:小于当前层最大值 或 当前层已经出现过的id
        vis[id]=dep;
        q.push({dep+1,val[nxt[id]],nxt[id]}); // 当前点的下一个
    }
}
int main()
{
    ios::sync_with_stdio(false);
    string s;
    cin>>T;
    for(int cas=1;cas<=T;cas++)
    {
        cin>>n>>s;
        mx=-1;
        for(int i=0;i<n;i++)
        {
            val[i]=s[i]-'0';
            mx=max(mx,val[i]);
            nxt[i]=int(((ll)i*(ll)i+1)%(ll)n); // i*i爆int
        }
        bfs();
        printf("Case #%d: ",cas);
        for(int i=0;i<n;i++)
            printf("%d",ans[i]);
        printf("\n");
    }
    return 0;
}
/*
1
60
986978694864376938576938576935213415791421412900638643325831
ans:
986868816868816868816868816868816868816868816868816868816868
*/

关于本题的时间复杂度

剪枝前可以认为最开始的起点最多有n个,然后它们需要扩展到n层,那么复杂度就是O(n2);

剪枝后的时间复杂度比较玄学,首先肯定和这些点组成的有向图有关,但是 i i i的后继点 ( i ∗ i + 1 ) % N (i*i+1)\%N (ii+1)%N可能导致走图中的环很快就能走到相同编号的点,然后被第二个剪枝条件剪掉。

参考
点i可以到达点(i*i+1)%n,取模会发生冲突像hash一样,而且由于本题的n很小,即hash表长小,并且如果权值=mx的点越多的话,冲突概率越大(剪枝效果越明显),比如x和y权值相同,但是x和y都走到z。于是我们可以增加一个剪枝:每一层要走的权值=mx的点用标记避免重复记录(即z只记录一次)

还有博主说是O(n*L),L为环的大小,不知道咋证明的…这个地方我只能说是玄学剪枝过了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nefu-ljw

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值