zoukankan      html  css  js  c++  java
  • BZOJ 4289 最短路+优化建图

    题意:给出一个N个点M条边的无向图,经过一个点的代价是进入和离开这个点的两条边的边权的较大值,求从起点1到点N的最小代价。起点的代价是离开起点的边的边权,终点的代价是进入终点的边的边权。

    解法:参考https://www.cnblogs.com/zj75211/p/7168254.html这位大佬的。学到了建图新姿势。

    首先还是先讲讲朴素建图:很容易想到拆点然后把边看成点,对于任意两条边a->b,b->c我们可以连一条权值为min(v1,v2)的a->c的边。容易看出这样的建图极限是n^2的,菊花图上会被卡成傻逼。我们要考虑优化建图:首先还是拆边然后把边看成点,然后我们枚举每一个中间点x(1<x<n),把x的出边按权值从小到大排序,然后小出边小大出边连权值差值的边,大出边向小出边连权值为0的边,x的每条入边向其对应的出边连权值为原边权的边,然后我们创造起点和终点:对于起点向其出边连权值原来的边,对于终点其入边向终点连权值原来的边。最后跑一次Dijkstra即可。

    这样的建图看起来有些诡异,我们不妨思考一下为什么这样是对的?其实很简单:因为这样建图能起到和朴素建图一样的作用,这种方法是巧妙的利用了差值起到了一种逐步累加变成正确边长的方法。入边向相应出边的连边保证了经过x点入边的代价,然后通过补差值保证了通过x点出边的代价。并且这样累加的方式可以变成任何一条原来的边,没有任何遗漏。

    只能说这样的建图确实十分巧妙,凭蒟蒻自己是想不到的,只能可遇不可求吧qwq。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef pair<LL,int> pii;
    const int N=4e5+10;
    int n,m,s,t;
    struct edge{ int x,y,z; }e[N<<2];
    vector<int> G[N];
    
    int cnt=1,head[N],nxt[N<<2],to[N<<2],len[N<<2];
    void add_edge(int x,int y,int z) {
        nxt[++cnt]=head[x]; to[cnt]=y; len[cnt]=z; head[x]=cnt;
    }
    
    bool cmp(int t1,int t2) { return e[t1].z<e[t2].z; }
    
    void Build() {
        for (int i=2;i<=2*m+1;i++) {  //起点和终点连边 
            if (e[i].x==1) add_edge(s,i,e[i].z);
            if (e[i].y==n) add_edge(i,t,e[i].z);
        }
        for (int i=2;i<n;i++) {
            sort(G[i].begin(),G[i].end(),cmp);
            for (int j=0;j<G[i].size();j++) {
                add_edge(G[i][j]^1,G[i][j],e[G[i][j]].z);  //i点入边向出边连边 
                if (j!=0) add_edge(G[i][j],G[i][j-1],0);  //大出边向小出边连0 
                if (j<G[i].size()-1)  //小出边向大出边连差值 
                    add_edge(G[i][j],G[i][j+1],e[G[i][j+1]].z-e[G[i][j]].z);
            }
        } 
    }
    
    priority_queue<pii> q;
    LL dis[N<<2];  bool vis[N<<2];
    LL Dijkstra() {
        memset(dis,0x3f,sizeof(dis));
        dis[s]=0; 
        q.push(make_pair(0,s));
        while (!q.empty()) {
            pii u=q.top(); q.pop();
            if (vis[u.second]) continue;
            vis[u.second]=1;
            for (int i=head[u.second];i;i=nxt[i]) {
                int y=to[i];
                if (dis[u.second]+len[i]<dis[y]) {
                    dis[y]=dis[u.second]+len[i];
                    q.push(make_pair(-dis[y],y));
                }
            }
        }
        return dis[t];    
    }
    
    int main()
    {
        cin>>n>>m;
        s=1; t=m*2+2;
        for (int i=1;i<=m;i++) {
            int x,y,z; scanf("%d%d%d",&x,&y,&z);
            e[i*2]=(edge){x,y,z};
            e[i*2+1]=(edge){y,x,z};
            G[x].push_back(i*2); G[y].push_back(i*2+1);  //G[i]保存i出边编号 
        }
        Build();
        cout<<Dijkstra()<<endl;
        return 0;
    }
  • 相关阅读:
    视频直播架构
    day1 python 入门
    python 多用户登录
    mysql innobackup 备份脚本
    ADT离线安装
    真机调试adb:wait for device 解决方案
    php中的魔术方法
    整理资料
    PostgreSQL表空间_数据库_模式_表_用户角色之间的关系[转]
    PHP获取文件夹的大小
  • 原文地址:https://www.cnblogs.com/clno1/p/11178154.html
Copyright © 2011-2022 走看看