• 建立虚点?一类暴力连边会超时的最短路问题


    最近在牛客和cf上面一连做到了两道类似的题目,是关于最短路算法优化问题的。

    https://ac.nowcoder.com/acm/contest/6885/E

    题目大意:

    给定 个点,第 个点有权值 。如果对于 不为 ,那么 间有无向边,边权为 。问从 的最短路。

    首先暴力连边的做法肯定是不行的,由于题目条件的原因,我们可以往进制方面去想。

    依次考虑每一个位,并且对当前位建立一个新点,设为 。遍历所有点,如果点 满足这一位上为 ,那么连一条从 的边,边权为这一位对应的二进制数。

    这样我们能够建立32个虚点,通过这些虚点,我们变相的达成了点与点之间连线,并且还能统计答案的任务。

    #include <bits/stdc++.h>
    #define debug freopen("r.txt","r",stdin)
    #define mp make_pair
    #define ri register int
    #define pb push_back
    #define lb(x) (x&(-x))
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef double lf;
    typedef pair<ll, ll> pii;
    const int maxn = 1e6+35;
    const int N = 1500+10;
    const ll INF = 0x3f3f3f3f3f3f3f3f;
    const int mod = 1e9+7;
    const int hash_num = 131;
    const double eps=1e-6;
    const double PI=acos(-1.0);
    inline ll read(){ll s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
    return s*w;}
    ll qpow(ll p,ll q){return (q&1?p:1)*(q?qpow(p*p%mod,q/2):1)%mod;}
    struct node{
        int to; ll dis;
        bool operator<(const node &b)const{
            return dis>b.dis;
        }
    };
    int n,t,a[maxn];
    bool vis[maxn];
    vector<int> v[maxn];
    
    ll dis[maxn];
    void dij(int s){ //s是起点,dis是结果
        memset(vis,0,sizeof(vis));
        memset(dis,INF,sizeof(dis)); 
        dis[s]=0; //last[s]=-1;
        static priority_queue<node> q; 
        q.push({s,0});
        while(!q.empty()){
            int u=q.top().to; q.pop();
            if(vis[u])continue; 
            vis[u]=1;
            for (ll j = 0; j < 32; ++j)
                if ((a[u]>>j)&1)
                {
                    for(auto p:v[j])
                    {
                        if(dis[p]>dis[u]+(1ll<<j)){
                            dis[p]=dis[u]+(1ll<<j);
                            q.push({p,dis[p]});
                            //last[p]=x; //last可以记录最短路(倒着)
                        }
                    }
                    v[j].clear();
                }
        }
    }
    int main()
    {
        #ifndef ONLINE_JUDGE
            debug;
        #endif
        t=read();
        while (t--)
        {
            n=read();
            for (int i = 0; i < 32; ++i) v[i].clear();
            for (int i = 1; i <= n; ++i)
            {
                a[i] =read();
            }
            for (int i = 1; i <= n; ++i)
            {
                for (ll j = 0; j < 32; ++j)
                {
                    if ((a[i]>>j)&1) v[j].pb(i);
                }
            }
            dij(1);
            if (dis[n] == INF) cout << "Impossible" <<endl;
                else cout << dis[n] <<endl;
        }
        return 0;
    }
    View Code

    https://codeforces.com/contest/1486/problem/E

    题目大意:

    给定 n 个点,m条边,如果从a城市,到b城市,再到c城市,花费,问从 1 到 1~n 的最短路。

    如果直接将两条边合成为一条边来重构图还是不行,注意到题目条件,边的价值很小,我们可以往价值方面去想。
    可以将每个点按照边权拆成51个虚点,编号0~50。对于每条边,如果边权为w,则从一个点的0号点连到另一个点的w号节点
    所以,边的个数就变成了 2m ,套上最短路模板跑即可得到答案。

    #include <bits/stdc++.h>
    #define debug freopen("r.txt","r",stdin)
    #define mp make_pair
    #define ri register int
    #define pb push_back
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef double lf;
    typedef pair<ll, ll> pii;
    const int maxn = 1e7+10;
    const int N = 1500+10;
    const int INF = 0x3f3f3f3f;
    const int mod = 1e9+7;
    const int hash_num = 131;
    const double eps=1e-6;
    const double PI=acos(-1.0);
    inline ll read(){ll s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
    return s*w;}
    ll qpow(ll p,ll q){return (q&1?p:1)*(q?qpow(p*p%mod,q/2):1)%mod;}
    struct node{
        int to; int dis;
        bool operator<(const node &b)const{
            return dis>b.dis;
        }
    };
    int n,m,u,v;
    bool vis[maxn];
    int dis[maxn],w;
    vector<node> a[maxn];
    void dij(int s){ //s是起点,dis是结果
        fill(vis,vis+n*51+1,0);
        fill(dis,dis+n*51+1,INF); dis[s]=0; //last[s]=-1;
        static priority_queue<node> q; q.push({s,0});
        while(!q.empty()){
            int x=q.top().to; q.pop();
            if(vis[x])continue; vis[x]=1;
            for(auto i:a[x]){
                int p=i.to;
                if(dis[p]>dis[x]+i.dis){
                    dis[p]=dis[x]+i.dis;
                    q.push({p,dis[p]});
                    //last[p]=x; //last可以记录最短路(倒着)
                }
            }
        }
    }
    int main()
    {
        #ifndef ONLINE_JUDGE
            debug;
        #endif
        n=read(),m=read();
        for (int i = 1; i <= m; ++i)
        {
            u=read(),v=read(),w=read();
            u--,v--;
            a[u*51].pb({v*51+w,0});
            for (int j = 1; j <= 50; ++j)
            {
                a[u*51+j].pb({v*51,(j + w) * (j + w)});
            }
            a[v*51].pb({u*51+w,0});
            for (int j = 1; j <= 50; ++j)
            {
                a[v*51+j].pb({u*51,(j + w) * (j + w)});
            }
        }
        dij(0);
        for (int i = 0; i < n ; ++i)
        {
            if (dis[i*51] == INF) cout << -1 << " ";
                else cout << dis[i*51] << " ";
        }
        cout <<endl;
        return 0;
    }
    View Code


    两个题目都是暴力连边会超时,但是通过题目的另一些条件,都可以建立虚点,从而降低复杂度。

  • 相关阅读:
    Vue之数据排序加签
    微信小程序之评分页面
    Vue之展示PDF格式的文档
    动态规划问题思考(DP)
    LitJson的使用
    c#事件管理器
    unity shader 学习
    unity ugui图片自适应文字内容大小
    unity3d各种OpenFileDialog操作
    ue4 使用3dsmax制作布料的插件及下载位置
  • 原文地址:https://www.cnblogs.com/Y-Knightqin/p/14437203.html
Copyright © 2020-2023  润新知