zoukankan      html  css  js  c++  java
  • kuangbin专题四 最短路练习【从入门到熟练】【13题】

    【POJ 2253 Frogger】

    这道题求从u到v中所有通路的最大边最小

    我直接二分做了,但实际上这种题是最短路算法的变种,意义在于告诉我们spfa这些算法不仅能维护出最短路,稍加修改后可以维护出很多其他东西。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<string>
    #include<stack>
    #include<fstream>
    #include<map>
    #include<iomanip> 
    #include<algorithm>
    #include<vector>
    #define INF 2e9
    #define maxnode 200000
    #define ll long long
    #define lowbit(x) (x&(-x))
    const int mod = 10007;
    const int MAXN = 2e2 + 10;
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    using namespace std;
    
    struct node{
        int v,d;
        node(int v1,int d1): v(v1),d(d1) {}
    };
    
    vector<node> edges[MAXN];
    
    int dist[MAXN],visit[MAXN],n;
    void spfa(){
        for(int i=1;i<=n;i++) dist[i]=INF;
        dist[1]=0; visit[1]=1;
        queue<int> q;
        q.push(1);
        while( !q.empty() ){
            int u = q.front(); q.pop();
            visit[u]=0;
            for(int i=0;i<edges[u].size();i++){
                int v = edges[u][i].v;
                if( dist[v]>dist[u]+edges[u][i].d ){
                    dist[v] = dist[u]+edges[u][i].d;
                    if( !visit[v] ) q.push(v);
                }
            }
        }
        
    }
    
    int x[MAXN],y[MAXN];
    double dis[MAXN][MAXN];
    
    int main(){
        //ios::sync_with_stdio(false);
        int tc=0;
        while(1){
            scanf("%d",&n);
            if(n==0) break;
            for(int i=1;i<=n;i++) scanf("%d%d",x+i,y+i);
            
            for(int i=1;i<=n;i++){
                for(int j=1;j<=n;j++){
                    double x1 = abs(x[i]-x[j]);
                    double y1 = abs(y[i]-y[j]);
                    dis[i][j] = sqrt(x1*x1 + y1*y1);
                    //cout<<i<<" "<<j<<" "<<dis[i][j]<<endl;
                }
            }
            
            double start=0,end=3000,mid;
            while( end-start>=0.00001 ){
                mid = (start+end)/2;
                for(int i=1;i<=n;i++) edges[i].clear();
                
                for(int i=1;i<=n;i++){
                    for(int j=1;j<=n;j++){
                        if( mid>=dis[i][j] ) edges[i].push_back( node(j,1) );
                    }
                }
                
                spfa();
                if( dist[2]!=INF ) end=mid;
                else start=mid;
            }
            printf("Scenario #%d
    ",++tc);
            printf("Frog Distance = %.3lf
    ",end);
            printf("
    ");
        }    
        
        return 0;
    }
    View Code

    【POJ 1797 Heavy Transportation】

    维护所有通路的最小边最大

    那修改后的松弛操作就是 dist[v] = max( dist[v], min( dist[u], edges[u][i].d ) )

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<string>
    #include<stack>
    #include<fstream>
    #include<map>
    #include<iomanip> 
    #include<algorithm>
    #include<vector>
    #define INF 2e9
    #define maxnode 200000
    #define ll long long
    #define lowbit(x) (x&(-x))
    const int mod = 10007;
    const int MAXN = 1e3 + 10;
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    using namespace std;
    
    struct node{
        int v,d;
        node(int v1,int d1): v(v1),d(d1) {}
    };
    
    vector<node> edges[MAXN];
    
    int dist[MAXN],visit[MAXN],n,m;//dist[i]代表从源点到i点中所有通路的最小边最大值 
    void spfa(){
        for(int i=1;i<=n;i++) dist[i]=0;
        dist[1]=INF; visit[1]=1;
        queue<int> q;
        q.push(1);
        while( !q.empty() ){
            int u = q.front(); q.pop();
            visit[u]=0;
            for(int i=0;i<edges[u].size();i++){
                int v = edges[u][i].v;
                int weight = min( edges[u][i].d,dist[u] );//weight是这条通路上的最小边 
                if( weight>dist[v] ){
                    dist[v] = weight;
                    if( !visit[v] ) {
                        //visit[v]=1;
                        q.push(v);
                    }
                }
            }
        }
        
    }
    
    //找所有通路中最小边最大的 
    int main(){
        //ios::sync_with_stdio(false);
        int t,tc=0; scanf("%d",&t); 
        while(t--){
            scanf("%d%d",&n,&m);
            for(int i=1;i<=m;i++){
                int u,v,d; scanf("%d%d%d",&u,&v,&d);
                edges[u].push_back( node(v,d) );
                edges[v].push_back( node(u,d) );
            }
            
            spfa();
            printf("Scenario #%d:
    ",++tc);
            printf("%d
    ",dist[n]);
            printf("
    ");
            
            for(int i=1;i<=n;i++) edges[i].clear();
        }    
        
        return 0;
    }
    View Code

    【POJ 3268 Silver Cow Party】

    想到跑flyod但n^3太慢就t了,实际上反向建边跑两边spfa就可以了!

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<string>
    #include<stack>
    #include<fstream>
    #include<map>
    #include<iomanip> 
    #include<algorithm>
    #include<vector>
    #define INF 2e9
    #define maxnode 200000
    #define ll long long
    #define lowbit(x) (x&(-x))
    const int mod = 10007;
    const int MAXN = 1e3 + 10;
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    using namespace std;
    
    struct node{
        int v,d;
        node(int v1,int d1): v(v1),d(d1) {}
    };
    vector<node> edges[MAXN];
    int vis[MAXN],dist1[MAXN],dist2[MAXN],n,m,x;
    
    void spfa1(){
        for(int i=1;i<=n;i++) dist1[i]=INF;
        queue<int> q;
        q.push(x); vis[x]=1; dist1[x]=0;
        while(!q.empty()){
            int u = q.front(); q.pop();
            vis[u]=0;
            for(int i=0;i<edges[u].size();i++){
                int v = edges[u][i].v;
                int d = edges[u][i].d;
                if( dist1[u]+d<dist1[v] ){
                    dist1[v]=dist1[u]+d;
                    if( !vis[v] ){
                        q.push(v);
                        vis[v]=1;
                    }
                }
            }
        }    
    }
    
    void spfa2(){
        for(int i=1;i<=n;i++) dist2[i]=INF;
        queue<int> q;
        q.push(x); vis[x]=1; dist2[x]=0;
        while(!q.empty()){
            int u = q.front(); q.pop();
            vis[u]=0;
            for(int i=0;i<edges[u].size();i++){
                int v = edges[u][i].v;
                int d = edges[u][i].d;
                if( dist2[u]+d<dist2[v] ){
                    dist2[v]=dist2[u]+d;
                    if( !vis[v] ){
                        q.push(v);
                        vis[v]=1;
                    }
                }
            }
        }
        
    }
    
    int u[100005],v[100005],d[100005];
    
    
    int main(){
        //ios::sync_with_stdio(false);
        while( scanf("%d%d%d",&n,&m,&x)!=EOF ){
            
            for(int i=1;i<=m;i++){
                scanf("%d%d%d",u+i,v+i,d+i);
                edges[ u[i] ].push_back( node(v[i],d[i]) );
            }
            
            spfa1();
            for(int i=1;i<=n;i++) edges[i].clear();    
            for(int i=1;i<=m;i++) edges[ v[i] ].push_back( node(u[i],d[i]) );
            spfa2();
            
            int ans=-1;
            for(int i=1;i<=n;i++){//枚举所有cows
                ans = max( ans,dist1[i]+dist2[i] );
            }
            printf("%d
    ",ans);
            
            for(int i=1;i<=n;i++) edges[i].clear();            
    
        }    
        
        return 0;
    }
    View Code

    【POJ 1860 Currency Exchange】

    题目问的是找正环,那改一下dist维护的数值就行了。

    但我一开始没看出来,所以写了bfs+剪枝也过了。后来我发现spfa好像也就是bfs+剪枝啊!维护的dist数组就是过程性剪枝,松弛就是剪枝的具体操作

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<string>
    #include<stack>
    #include<fstream>
    #include<map>
    #include<iomanip> 
    #include<algorithm>
    #include<vector>
    #define INF 2e9
    #define maxnode 200000
    #define ll long long
    #define lowbit(x) (x&(-x))
    const int mod = 10007;
    const int MAXN = 1e2 + 10;
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    using namespace std;
    
    struct exchange{
        int type;
        double r,c;
        exchange(int t1,double r1,double c1): type(t1),r(r1),c(c1) {}
    };
    
    vector<exchange> edges[MAXN];//edges[i]是拿着i货币能选择的一些兑换方案 
    
    double d[MAXN],money;
    int n,m,s;
    
    struct node{
        int type;
        double m;//状态是拿着m个type钱
        node(int t1,double m1): type(t1),m(m1) {} 
    };
    
    bool bfs(){
        for(int i=1;i<=n;i++) d[i]=-INF;//最优性剪枝,d[i]为i种货币最多有多少个 
        queue<node> q;
        q.push( node(s,money) ); d[s]=money;
        while( !q.empty() ){
            node p = q.front(); q.pop();
            if( p.type==s && p.m>money ) return true;
            for(int i=0;i<edges[p.type].size();i++){//能怎么换 
                exchange e = edges[p.type][i];//在这个所换 
                double m1 = (p.m-e.c)*e.r;
                if( m1>d[ e.type ] ){
                    d[e.type]=m1;
                    q.push( node(e.type,m1) );
                }
            }
        }
    
        return false;
    }
    
    int main(){
        //ios::sync_with_stdio(false);
        while( scanf("%d%d%d%lf",&n,&m,&s,&money)!=EOF ){
            
            for(int i=1;i<=m;i++){
                int a,b; 
                double r1,c1,r2,c2; scanf("%d%d%lf%lf%lf%lf",&a,&b,&r1,&c1,&r2,&c2);
                edges[a].push_back( exchange(b,r1,c1) );
                edges[b].push_back( exchange(a,r2,c2) );
            }
            
            if( bfs() ) printf("YES
    ");
            else printf("NO
    ");
            
            for(int i=1;i<=n;i++) edges[i].clear();
        }    
        
        return 0;
    }
    View Code

    【POJ 3660 Cow Contest】

    难点在于建模,b比a弱,就建一条有向边从b到a。定义a能确定排名的意思为对于所有的其他人,要么我能到他(我比他弱),要么他能到我(我比他强),所以跑一次flyod就行了

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<string>
    #include<stack>
    #include<fstream>
    #include<map>
    #include<iomanip> 
    #include<algorithm>
    #include<vector>
    #define INF 2e9
    #define maxnode 200000
    #define ll long long
    #define lowbit(x) (x&(-x))
    const int mod = 10007;
    const int MAXN = 1e2 + 10;
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    using namespace std;
    
    
    ll dist[MAXN][MAXN];
    
    int main(){
        //ios::sync_with_stdio(false);
        int n,m;
        while( scanf("%d%d",&n,&m)!=EOF ){
    
            for(int i=1;i<=n;i++){
                for(int j=1;j<=n;j++){
                    if( i==j ) continue;
                    else dist[i][j]=INF;
                }
            }
            
            
            for(int i=1;i<=m;i++){
                int u,v; scanf("%d%d",&u,&v);
                //u比v强,从v到u有一条边
                dist[v][u]=1;
            }
            
            for(int k=1;k<=n;k++){
                for(int i=1;i<=n;i++){
                    for(int j=1;j<=n;j++){
                        dist[i][j] = min( dist[i][j],dist[i][k]+dist[k][j] );
                    }
                }
            }
            
            int ans=0;
            for(int i=1;i<=n;i++){
                bool flag=true;//假设排名确定 
                for(int j=1;j<=n;j++){
                    if( dist[i][j]!=INF || dist[j][i]!=INF  ) continue;
                    flag=false;
                }
                if( flag ) ans++;
            }
            
            printf("%d
    ",ans);
            
        }
    
        
        return 0;
    }
    View Code

    【POJ 2240 Arbitrage】

    找正环模板

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<string>
    #include<stack>
    #include<fstream>
    #include<map>
    #include<iomanip> 
    #include<algorithm>
    #include<vector>
    #define INF 2e9
    #define maxnode 200000
    #define ll long long
    #define lowbit(x) (x&(-x))
    const int mod = 10007;
    const int MAXN = 1e2 + 10;
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    using namespace std;
    
    map<string,int> mp; 
    struct node{
        int v;
        double rate;
        node(int v1,double r): v(v1),rate(r) {}
    };
    
    vector<node> edges[MAXN];
    
    double dist[MAXN];//dist[i]指的是从起点到 i 所拥有的最多i货币个数 
    int n,m;
    int vis[MAXN],update[MAXN];
    
    bool spfa(){//找正环 
        memset(dist,0,sizeof(dist));
        memset(vis,0,sizeof(vis));
        memset(update,0,sizeof(update));
        
        queue<int> q;
        q.push(1); vis[1]=1; update[1]++;
        dist[1]=1;
        while( !q.empty() ){
            int u=q.front(); q.pop();
            //cout<<u<<endl;
            //for(int i=1;i<=n;i++) cout<<dist[i]<<" "; cout<<endl<<endl;
            vis[u]=0;
            for(int i=0;i<edges[u].size();i++){
                double r = edges[u][i].rate;
                int v = edges[u][i].v;
                if( dist[u]*r >= dist[v] ){
                    dist[v] = dist[u]*r;
                    if( !vis[v] ){
                        q.push(v);
                        vis[v]=1;
                        update[v]++;
                        if( update[v]>=n ) return true;
                    }                    
        
                }        
            }
        }
        return false;
        
    }
    
    int main(){
        ios::sync_with_stdio(false);
        int tc=0;
        while(1){
            cin>>n;
            if(n==0) break;
            for(int i=1;i<=n;i++){
                string s; cin>>s;
                mp[s]=i;//s货币的id是i 
            }
            cin>>m;
            for(int i=1;i<=m;i++){
                string a,b; double rate;
                cin>>a>>rate>>b;
                edges[ mp[a] ].push_back( node(mp[b],rate) );
            }
            //Case 1: Yes
            if( spfa() ) cout<<"Case "<<++tc<<": Yes"<<endl;
            else cout<<"Case "<<++tc<<": No"<<endl;
    
            for(int i=1;i<=n;i++) edges[i].clear();
        }
    
        
        return 0;
    }
    View Code

    【POJ 3159 Candies】

    首先要建出模型题目问的是1到n的最短路(维护dist数组,dist[i]表示1号同学最多可以比i同学少多少颗糖果,然后发现松弛的方法跟最短路方法一样)

    之后是卡常,通过看题解知道要用前向星+栈维护才可以过,算是体现出了spfa的不稳定性和其算法本质是搜索(模拟dfs)。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<string>
    #include<stack>
    #include<fstream>
    #include<map>
    #include<iomanip> 
    #include<algorithm>
    #include<vector>
    #define INF 2e9
    #define maxnode 200000
    #define ll long long
    #define lowbit(x) (x&(-x))
    const int mod = 10007;
    const int MAXN = 3e4 + 10;
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    using namespace std;
    
    struct Edge{
        int v,d,next;
    }edges[150000];
    
    int head[MAXN],cnt;
    
    void add_edge(int u,int v,int d){//建一条从u到v的边 
        edges[++cnt].next = head[u];
        head[u]=cnt;
        edges[cnt].v = v; 
        edges[cnt].d = d;
    }
    
    int dist[MAXN],vis[MAXN],n,m;//dist[i]代表snoopy最多比i同学少多少颗糖果 
    
    void spfa(){
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=n;i++) dist[i]=INF;
        
        stack<int> q;
        while( !q.empty() ) q.pop();
        
        q.push(1); vis[1]=1;
        dist[1]=0;
        while( !q.empty() ){
            int u=q.top(); q.pop();
            vis[u]=0;
            for(int i=head[u];i;i=edges[i].next){
                int d = edges[i].d;
                int v = edges[i].v;
                if( dist[v]>dist[u]+d ){
                    dist[v] = dist[u]+d;
                    if( !vis[v] ){
                        q.push(v);
                        vis[v]=1;
                    }                    
                }        
            }
        }
    
    }
    
    int main(){
        //ios::sync_with_stdio(false);
        
        while( scanf("%d%d",&n,&m)!=EOF ){
            
            cnt=0;
            memset(head,0,sizeof(head));
            
            for(int i=1;i<=m;i++){
                int u,v,d; scanf("%d%d%d",&u,&v,&d);
                add_edge(u,v,d);
            }
            
            spfa();
            printf("%d
    ",dist[n]);
        }
    
        
        return 0;
    }
    View Code

    【POJ 2502 Subway】

    根据题意建图就行,输入比题难写。poj里g++会wa,但c++就过了

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<math.h>
    #include<queue>
    #include<string>
    #include<stack>
    #include<fstream>
    #include<map>
    #include<iomanip> 
    #include<algorithm>
    #include<vector>
    #define INF 2e9
    #define maxnode 200000
    #define ll long long
    #define lowbit(x) (x&(-x))
    const int mod = 10007;
    const int MAXN = 2e2 + 10;
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    using namespace std;
    
    struct stop{
        int x,y;
        stop(int x1=0,int y1=0):x(x1),y(y1) {}
    }stops[MAXN];
    
    vector<stop> line;
    
    struct edge{
        int v,next;//next指的是相同u情况下的下一条边 
        double d;
    }edges[100000];
    int head[MAXN],cnt;
    
    void add_edge(int u,int v,double d){
        edges[++cnt].next=head[u];
        edges[cnt].v=v;
        edges[cnt].d=d;
        head[u] = cnt;
    }
    
    double dist[MAXN];
    int vis[MAXN];
    
    void spfa(){
        for(int i=1;i<MAXN;i++) dist[i]=INF;
        queue<int> q;
        q.push(1); vis[1]=1; dist[1]=0;
        while( !q.empty() ){
            int u = q.front(); q.pop();
            vis[u]=0;
            for(int i=head[u];i;i=edges[i].next){
                int v = edges[i].v;
                double d = edges[i].d;
                if( dist[v]>dist[u]+d ){
                    dist[v] = dist[u]+d;
                    if( !vis[v] ){
                        vis[v]=1;
                        q.push(v);
                    }
                }
            }
    
        }
        
    }
    
    int main(){
        //ios::sync_with_stdio(false);
        //freopen("1.in","r",stdin);
        //freopen("1.out","w",stdout);
        //1是起点,2是终点 
        int x1,y1,x2,y2;
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2 );
        
        stops[1] = stop(x1,y1);
        stops[2] = stop(x2,y2);
        
        int xcha = abs(x1-x2);
        int ycha = abs(y1-y2);
        double da = sqrt( 1.0*xcha*xcha+1.0*ycha*ycha )/10000.0*60;
        add_edge(1,2,da);
        add_edge(2,1,da);
        
        int id=2,num=0;
        while( scanf("%d%d",&x1,&y1)!=EOF ){
            if( x1==-1 && y1==-1 ) num=0;//目前lines上有几个 
            else{
                num++; id++;//目前stop的id
                stops[id]=stop(x1,y1);
                //将它与lines上的上一个stop建双向边,与所有不在lines上的其他stop建双向边
                if( num>1 ){
                    for(int i=1;i<id-1;i++){
                        int x = abs(stops[i].x-x1);
                        int y = abs(stops[i].y-y1);
                        double dis = sqrt( 1.0*x*x+1.0*y*y )/10000.0*60;
                        add_edge(i,id,dis);
                        add_edge(id,i,dis); 
                    }
                    
                    int x = abs(stops[id-1].x-x1);
                    int y = abs(stops[id-1].y-y1);
                    double dis = sqrt( 1.0*x*x+1.0*y*y )/40000.0*60;
                    add_edge(id-1,id,dis);
                    add_edge(id,id-1,dis);
                }
                else{
                    for(int i=1;i<id;i++){
                        int x = abs(stops[i].x-x1);
                        int y = abs(stops[i].y-y1);
                        double dis = sqrt( 1.0*x*x+1.0*y*y )/10000.0*60;
                        add_edge(i,id,dis);
                        add_edge(id,i,dis); 
                    }
                } 
                 
            }
        }
        spfa();
        printf("%.0lf
    ",dist[2] );    
        
        return 0;
    }
    View Code

    【POJ 1062 昂贵的婚礼】

    真的是什么算法都能跟暴力扯上关系。。以后要多长个心眼,就像二分一样,枚举也是ACM常用的套路一种啊!

    逐渐的建图有点感觉了,这题的难点在于解决【等级限制】的问题。kuangbin专题里的分水槛的题终于来了

    我一开始想的是改变边的构造,想的是如果某某交易发生了,那就自动有一些边之后不能再走。这个感觉是对的,但没办法实现。实际上可以从点出发去考虑,考虑哪些点会在这个图里出现,那么等级差不超过M的点会在图里出现。再加上已知level[1](酋长的等级)必须在图里出现,我们从[ level[1]-m, level[1] ], [ level[1]-m+1, level[1]+1 ] , ... 枚举到 [ level[1] , level[1]+M ]就行了。那么每次找最短路就只考虑主人等级落在这个区间里的点

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<math.h>
    #include<queue>
    #include<string>
    #include<stack>
    #include<fstream>
    #include<map>
    #include<iomanip> 
    #include<algorithm>
    #include<vector>
    #define INF 2e9
    #define maxnode 200000
    #define ll long long
    #define lowbit(x) (x&(-x))
    const int mod = 10007;
    const int MAXN = 1e2 + 10;
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    using namespace std;
    
    struct edge{
        int v,next;//next指的是相同u情况下的下一条边 
        int d;
    }edges[100000];
    
    int head[MAXN],cnt;
    
    void add_edge(int u,int v,int d){
        edges[++cnt].next=head[u];
        edges[cnt].v=v;
        edges[cnt].d=d;
        head[u] = cnt;
    }
    
    int dist[MAXN],vis[MAXN],include[MAXN],n,m;
    
    void spfa(){
        for(int i=1;i<=n;i++) dist[i]=INF;
        queue<int> q;
        q.push(0); vis[0]=1; dist[0]=0;
        while( !q.empty() ){
            int u = q.front(); q.pop();
            vis[u]=0;
            for(int i=head[u];i;i=edges[i].next){
                int v = edges[i].v;
                int d = edges[i].d;
                if( dist[v]>dist[u]+d && include[v] ){
                    dist[v] = dist[u]+d;
                    if( !vis[v] ){
                        vis[v]=1;
                        q.push(v);
                    }
                }
            }
    
        } 
    }
    
    int level[MAXN];
    
    int main(){
        //ios::sync_with_stdio(false);
        //freopen("1.in","r",stdin);
        //freopen("1.out","w",stdout);
        //0是起点 代表什么都没有 
        //1是终点 代表有了聘礼 
        while( scanf("%d%d",&m,&n)!=EOF ){
            for(int i=1;i<=n;i++){
                int P,L,X; scanf("%d%d%d",&P,level+i,&X);
                add_edge(0,i,P);//不用优惠政策楞头买 
                for(int j=1;j<=X;j++){//X个替代品 
                    int t,v; scanf("%d%d",&t,&v);
                    add_edge(t,i,v);//可以花v的钱,从拥有t变成拥有i 
                }
            }
            
            int ans=INF;
            for(int i=level[1]-m;i<=level[1];i++){
                for(int j=1;j<=n;j++){
                    if( level[j]<i || level[j]>i+m ) include[j]=0;//j结点不考虑在图里面 
                    else include[j]=1;     
                }
                spfa();
                ans=min(ans,dist[1]);
            }
            printf("%d
    ",ans);
            cnt=0;
        }
        
        
        return 0;
    }
    View Code

    【HDU 4725 The Shortest Path in Nya Graph】

    我一开始直接耿直建图了,开个vector每层的结点往上下两层建边,那直接边的数量变成n^2级别的了,所以tle。要建图优化

    就是每层抽象成一个结点,这样一共有2*n个结点,其中1-n代表的是层结点,n+1-n+n代表的才是正常的结点。

    那建图就是每层层结点之间建边代价c(双向边所以共2*n条)

    此外再由于【每层的结点是汇点】(指的是层结点的入度只能由非本层的结点指过来),所以向本层的所有其他结点建单向代价为0的边(共n条)

    然后原本的结点要向上一层或下一层的层结点建代价为c的边(共2*n条,严格点第一层和最后一层单独考虑,但无所谓了)

    所以这么下来5*n级别的边就建完图了,稠密图优化成稀疏图!

    还有用spfa的话【建图的顺序】会被卡!如果最后建层结点之间的边会tle,但先建就能ac。玄学。。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<math.h>
    #include<queue>
    #include<string>
    #include<stack>
    #include<fstream>
    #include<map>
    #include<iomanip> 
    #include<algorithm>
    #include<vector>
    #define INF 2e9
    #define maxnode 200000
    #define ll long long
    #define lowbit(x) (x&(-x))
    const int mod = 10007;
    const int MAXN = 2e5 + 10;
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    using namespace std;
    
    struct edge{
        int v,next;//next指的是相同u情况下的下一条边 
        int d;
    }edges[10*MAXN];
    
    int head[MAXN],cnt,a[MAXN];
    
    void add_edge(int u,int v,int d){
        edges[++cnt].next=head[u];
        edges[cnt].v=v;
        edges[cnt].d=d;
        head[u] = cnt;
    }
    
    int dist[MAXN],vis[MAXN],n,m,c,layer[MAXN];
    
    void spfa(){
        for(int i=1;i<=2*n;i++) dist[i]=INF;
        memset(vis,0,sizeof(vis));
        
        queue<int> q;
        q.push(n+1); vis[n+1]=1; dist[n+1]=0;
        while( !q.empty() ){
            int u = q.front(); q.pop();
            vis[u]=0;
            for(int i=head[u];i;i=edges[i].next){
                int v = edges[i].v;
                int d = edges[i].d;
                if( dist[v]>dist[u]+d ){
                    dist[v] = dist[u]+d;
                    if( !vis[v] ){
                        vis[v]=1;
                        q.push(v);                
                    }
                }
            }
    
        } 
    }
    
    int flag[MAXN];
    
    int main(){    
        //ios::sync_with_stdio(false);
        //freopen("1.in","r",stdin);
        //freopen("1.out","w",stdout);
        int t,tc=0; scanf("%d",&t);
        
        //1到n是layor结点,n+1到n+n才是其他结点
        while( t-- ){
            cnt=0;
            memset(head,0,sizeof(head));
            memset(flag,0,sizeof(flag));
            
            scanf("%d%d%d",&n,&m,&c);
            for(int i=1;i<=n;i++){//2*n
                scanf("%d",layer+i);
                flag[ layer[i] ]=1;
            }
            
            
            //这两个建边交换顺序的话会tle。。。 
            for(int i=1;i<n;i++){
                if( flag[i] && flag[i+1] ){
                    add_edge(i,i+1,c);
                    add_edge(i+1,i,c);
                }
            } 
            
            for(int i=1;i<=n;i++){            
                add_edge(layer[i],n+i,0);
                if( layer[i]==1 ) add_edge(n+i,2,c);
                else if( layer[i]==n ) add_edge(n+i,n-1,c);
                else{
                    add_edge(n+i,layer[i]+1,c);
                    add_edge(n+i,layer[i]-1,c);
                }
            }
            //
            
            for(int i=1;i<=m;i++){
                int u,v,w; scanf("%d%d%d",&u,&v,&w);
                add_edge(u+n,v+n,w);
                add_edge(v+n,u+n,w);
            }
            
            spfa();
    
            if( dist[n+n]==INF ) dist[n+n]=-1;
            printf("Case #%d: %d
    ",++tc,dist[n+n]);
        }
        
        
        return 0;
    }
    View Code

    【HDU 3416 Marriage Match IV】

    被搞自闭。。

    但做这一题的收获真的大,练习了写dij想明白复杂度是m+vlogv,相比而言spfa是km,所以相较之下还是dij最稳,其实做了上一题也是让我正视了spfa,将其打下神坛。

    然后是会了生成一个最短路图,使得原图的所有最短路都包含在其中。也就是满足dist1[u] + c + dist2[v] == dist1[B]的边在最短路上(因为如果一条边在最短路上,那该最短路一定有 A->u + u->v + v->B的形式。)

    接下来跑最大流就好了,写最大流的时候发现了一个问题,一是拿dfs,bfs写的话会比较好;因为拿stack模拟dfs写的话,若用邻接表增广的时候比较麻烦(因为你不知道用的是哪条边,开个vector不断记录就不优雅);若用邻接矩阵的会费用流重边就没法做了。所以拿dfs+bfs最好。其次学会了炸点优化也觉得挺妙的,具体实现的时候由于理解不深还遇到并解决了一个问题。

    就是如果在这里写edges[i].cap的话会tle,而且会导致死循环而不是常数上的变慢,为什么呢?

    因为如果这条边的cap<0的话,仅仅代表的是这一个弧用不了,那next的可能还可以啊,所以不能退出循环。会死循环是因为bfs的时候发现能到汇点,但dfs的时候又有的边走不到所以走不到汇点增广为0,因此就卡在那了。

    #include<iostream>
    #include<cstring>
    #include<queue>
    #define inf 2e9
    #define maxnode 200000
    #define ll long long
    #define lowbit(x) (x&(-x))
    const int mod = 10007;
    const int maxn = 2e3 + 10;
    const int maxm = 2e5 + 10;
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    using namespace std;
    
    int n,m,cnt,head[maxn],s,t;
    int dist1[maxn],dist2[maxn],vis[maxn];
    struct edge{
        int v,next,d;
    }edges[maxm];
    
    void addedge(int u,int v,int d){
        cnt++;
        edges[cnt].v=v;
        edges[cnt].d=d;
        edges[cnt].next=head[u];
        head[u]=cnt;
    }
    
    struct node{
        int v,dis;
        node(int v1,int d1): v(v1),dis(d1) {}
        bool operator > (const node &p)const{
            return dis>p.dis;
        }
    };
    
    void dij1(){    
        for(int i=1;i<=n;i++) dist1[i]=inf;
        memset(vis,0,sizeof(vis));
        
        priority_queue<node,vector<node>,greater<node> > q; while(!q.empty()) q.pop();
        q.push(node(s,0)); dist1[s]=0;
        while( !q.empty() ){
            node u=q.top(); q.pop();
            if( vis[u.v] ) continue;
            vis[u.v]=1;
            for(int i=head[u.v];i;i=edges[i].next){
                int v=edges[i].v;
                if( dist1[v]>dist1[u.v]+edges[i].d ){
                    dist1[v]=dist1[u.v]+edges[i].d;
                    q.push(node(v,dist1[v]));
                }
            }
        }
    }
    
    void dij2(){    
        for(int i=1;i<=n;i++) dist2[i]=inf;
        memset(vis,0,sizeof(vis));
        
        priority_queue<node,vector<node>,greater<node> > q; while(!q.empty()) q.pop();
        q.push(node(t,0)); dist2[t]=0;
        while( !q.empty() ){
            node u=q.top(); q.pop();
            if( vis[u.v] ) continue;
            vis[u.v]=1;
            for(int i=head[u.v];i;i=edges[i].next){
                int v=edges[i].v;
                if( dist2[v]>dist2[u.v]+edges[i].d ){
                    dist2[v]=dist2[u.v]+edges[i].d;
                    q.push(node(v,dist2[v]));
                }
            }
        }
    }
    
    int u[maxm],v[maxm],c[maxm];
    
    struct Edge{
        int v,next,cap,reverse;
    }Edges[2*maxm];
    
    void add_edge(int u,int v,int cap){
        cnt++;
        Edges[cnt].v=v;
        Edges[cnt].cap=cap;
        Edges[cnt].next=head[u];
        Edges[cnt].reverse=cnt+1;
        head[u]=cnt;
        cnt++;
        Edges[cnt].v=u;
        Edges[cnt].cap=0;
        Edges[cnt].next=head[v];
        Edges[cnt].reverse=cnt-1;
        head[v]=cnt;
    }
    
    int layor[maxn];
    bool bfs(){
        memset(layor,-1,sizeof(layor));
        queue<int> q; while(!q.empty()) q.pop();
        q.push(s); layor[s]=0;
        while( !q.empty() ){
            int u=q.front(); q.pop();
            for(int i=head[u];i;i=Edges[i].next){
                int v=Edges[i].v;
                if( layor[v]==-1 && Edges[i].cap ){
                    layor[v]=layor[u]+1;
                    if( v==t )return true;
                    else q.push(v);
                }
            }
        }
        return false;
    }
    
    int dfs(int u,int lim){//lim是目前的最大流量,返回要流多少
        if( u==t || lim==0 ) return lim;
        int flow=0;//目前要流0
        for(int i=head[u];i;i=Edges[i].next){
            Edge &e = Edges[i];
            if( layor[e.v]==layor[u]+1 && Edges[i].cap ){
                int x=dfs(e.v,min(lim,e.cap));
                lim-=x; flow+=x;
                e.cap-=x; Edges[e.reverse].cap+=x;
            }
        }
        if( !flow ) layor[u]=-2;
        return flow;
    } 
    
    int maxflow(){
        int ans=0;
        while( bfs() ) ans+=dfs(s,inf);
        return ans;
    }
    
    
    int main(){
        int T; scanf("%d",&T);
        while( T-- ){
            scanf("%d%d",&n,&m);
            
            cnt=0; 
            memset(head,0,sizeof(head));
            for(int i=1;i<=m;i++){
                scanf("%d%d%d",u+i,v+i,c+i);
                addedge(u[i],v[i],c[i]); 
            }
            scanf("%d%d",&s,&t);
    
            dij1();
            cnt=0; memset(head,0,sizeof(head));
            for(int i=1;i<=m;i++) addedge(v[i],u[i],c[i]); 
            dij2();
            
            cnt=0; memset(head,0,sizeof(head));
            for(int i=1;i<=m;i++){
                // s->u[i] , u[i]->v[i] , v[i]->t
                if( dist1[u[i]]+c[i]+dist2[v[i]]==dist1[t] ) add_edge(u[i],v[i],1);
            }
            printf("%d
    ",maxflow());
        }
        
        return 0;
    }
    View Code

    【POJ 4370 0 or 1】

    智商题啊。。

    考虑x矩阵中x[i][j]=1在图里面的意义是i和j之间的边被走,那么x矩阵里的第i行代表第i个点的出度情况,第i列代表第i个点的入度情况:

    1.第一行除第一个外有且有一个1 ==> 所以1只能出去一次,1号节点出度为1

    2.第n列除最后一个外有且有一个1==>所以n只能被其他点到一次,n号结点入度为1

    3. 第2-n-1行的和等于其列的和 ==> 2-n-1号结点出度等于入度

    除了最短路外,还要想到包含1的最小环+包含n的最小环也是满足上述情况的。这样才做完

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<string>
    #include<stack>
    #include<fstream>
    #include<map>
    #include<iomanip> 
    #include<algorithm>
    #include<vector>
    #define inf 2e9
    #define maxnode 200000
    #define ll long long
    #define lowbit(x) (x&(-x))
    const int mod = 10007;
    const int maxn = 3e2 + 10;
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    using namespace std;
    
    int n;
    int G[305][305];
    
    int vis[maxn],dist[maxn];
    int spfa(int s,int e){
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=n;i++) dist[i]=inf;
        
        queue<int> q;
        if( s==e ){//找最小环 
            for(int i=1;i<=n;i++){
                if( i!=s ){
                    q.push(i);
                    vis[i]=1;
                    dist[i]=G[s][i];
                }
            }
        }
        else{
            q.push(s);
            vis[s]=1; dist[s]=0;
        }
    
        while(!q.empty()){
            int u=q.front(); q.pop();
            vis[u]=0;
            for(int i=1;i<=n;i++){
                if( dist[i]>dist[u]+G[u][i] ){
                    dist[i]=dist[u]+G[u][i];
                    if( !vis[i] ){
                        q.push(i);
                        vis[i]=1;
                    }
                }
            }
        }
        return dist[e];
    } 
    
    int main(){
        
        while( scanf("%d",&n)!=EOF ){
            
            for(int i=1;i<=n;i++){
                for(int j=1;j<=n;j++){
                    scanf("%d",&G[i][j]); 
                }
            }
            
            printf("%d
    ",min(spfa(1,n),spfa(1,1)+spfa(n,n)));
        }
        
        return 0;
    }
    View Code

    【POJ 3169 Layout】

    这题跟之前的candies有点像,也是差分约束。

    首先看它有两个约束条件ML和MD,那就很烦,我们可以一个一个考虑。ML代表的是A,B最远离多少,那么如果只有ML这一个约束的话,题目就跟candies一模一样了;这个时候再考虑MD,表示A,B最近隔多远;由于我们想维护的数组dist[i]代表1与i之间的最远距离,然后如果原来知道了dist[B]的话,那么我们可以推出来1到A的最远距离是dist[B]-D,所以从B到A建一条权值为-D的边就行了。

    最后考虑如果有负环的话那就是no possible layout,因为负环代表有一个点dist[i]<0,现实意义是离1最远差一个负数,明显说不通;

    如果没有路到dist[n]那就是多远都可以,因为没有有效约束能约束到终点。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<string>
    #include<stack>
    #include<fstream>
    #include<map>
    #include<iomanip> 
    #include<algorithm>
    #include<vector>
    #define inf 2e9
    #define maxnode 200000
    #define ll long long
    #define lowbit(x) (x&(-x))
    const int mod = 10007;
    const int maxn = 1e3 + 10;
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    using namespace std;
    
    int n,m1,m2,cnt,head[maxn],s,t;
    struct edge{
        int v,next,d;
    }edges[30010];
    
    void addedge(int u,int v,int d){
        cnt++;
        edges[cnt].v=v;
        edges[cnt].next=head[u];
        edges[cnt].d=d;
        head[u]=cnt;
    }
    
    int vis[maxn],dist[maxn],update[maxn];
    bool spfa(){
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=n;i++) dist[i]=inf;
        memset(update,0,sizeof(update));
        
        queue<int> q;
        q.push(s); vis[s]=1; dist[s]=0;
        while(!q.empty()){
            int u=q.front(); q.pop();
            vis[u]=0;
            for(int i=head[u];i;i=edges[i].next){
                int v=edges[i].v;
                if( dist[v]>dist[u]+edges[i].d ){
                    dist[v]=dist[u]+edges[i].d;
                    if( !vis[v] ){
                        q.push(v);
                        update[v]++;
                        if( update[v]>=n ) return true;
                    }
                }
            }
        }
        return false;
    } 
    
    int main(){
        
        while( scanf("%d%d%d",&n,&m1,&m2)!=EOF ){
            cnt=0; 
            memset(head,0,sizeof(head));
            int u,v,c; 
            
            for(int i=1;i<=m1;i++){
                scanf("%d%d%d",&u,&v,&c);
                addedge(u,v,c); 
            }
            for(int i=1;i<=m2;i++){
                scanf("%d%d%d",&u,&v,&c);
                addedge(v,u,-c);
            }
            
            s=1; t=n;
            //有负环-1,到不了t -2,其他dist[t] 
            if( spfa() ) printf("-1
    ");
            else if( dist[t]==inf ) printf("-2
    ");
            else printf("%d
    ",dist[t]);
            
            
        }
        
        return 0;
    }
    View Code

     最后总结一下最短路专题,感觉其本质是dfs+剪枝,因为松弛时用的dist数组实际上就是过程性剪枝的一种。也正因为如此,所以spfa,dij模板可以维护出除最短路外的很多东西。由于spfa很方便,在我心目中前面的题把spfa送上神坛,但由于该算法的不稳定,后面的题又把其打下神坛。。。然后是最短路的一些变种,如分层/等级限制/差分。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<string>
    #include<stack>
    #include<fstream>
    #include<map>
    #include<iomanip> 
    #include<algorithm>
    #include<vector>
    #define inf 2e9
    #define maxnode 200000
    #define ll long long
    #define lowbit(x) (x&(-x))
    const int mod = 10007;
    const int maxn = 1e3 + 10;
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    using namespace std;
    
    int n,m1,m2,cnt,head[maxn],s,t;
    struct edge{
        int v,next,d;
    }edges[30010];
    
    void addedge(int u,int v,int d){
        cnt++;
        edges[cnt].v=v;
        edges[cnt].next=head[u];
        edges[cnt].d=d;
        head[u]=cnt;
    }
    
    int vis[maxn],dist[maxn],update[maxn];
    bool spfa(){
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=n;i++) dist[i]=inf;
        memset(update,0,sizeof(update));
        
        queue<int> q;
        q.push(s); vis[s]=1; dist[s]=0;
        while(!q.empty()){
            int u=q.front(); q.pop();
            vis[u]=0;
            for(int i=head[u];i;i=edges[i].next){
                int v=edges[i].v;
                if( dist[v]>dist[u]+edges[i].d ){
                    dist[v]=dist[u]+edges[i].d;
                    if( !vis[v] ){
                        q.push(v);
                        update[v]++;
                        if( update[v]>=n ) return true;
                    }
                }
            }
        }
        return false;
    } 
    
    int main(){
        
        while( scanf("%d%d%d",&n,&m1,&m2)!=EOF ){
            cnt=0; 
            memset(head,0,sizeof(head));
            int u,v,c; 
            
            for(int i=1;i<=m1;i++){
                scanf("%d%d%d",&u,&v,&c);
                addedge(u,v,c); 
            }
            for(int i=1;i<=m2;i++){
                scanf("%d%d%d",&u,&v,&c);
                addedge(v,u,-c);
            }
            
            s=1; t=n;
            //有负环-1,到不了t -2,其他dist[t] 
            if( spfa() ) printf("-1
    ");
            else if( dist[t]==inf ) printf("-2
    ");
            else printf("%d
    ",dist[t]);
            
            
        }
        
        r
  • 相关阅读:
    splinter webdriver API 的基本实现
    201253 线程和进程的区别
    Winform中的默认图片
    [收藏】正确使用SqlConnection对象,兼谈数据库连接池
    手机相关的基础名称
    常见排序
    SIP相关内容
    How to set the WIFI configuration
    本地化的设置和读取
    Serialize And Deserialize Binary Tree
  • 原文地址:https://www.cnblogs.com/ZhenghangHu/p/9869298.html
Copyright © 2011-2022 走看看