最短路题


340. 通信线路

题意:

给定n个点m条边的无向图,边有边权。
你可以指定一条从点1到点n的路径,最多将k条边的权值变为0,然后这条路径的代价为路径上剩余边的最大边权。
问从1到n的最小花费是多少,如果1无法到达n则输出-1

数据范围:n,k<=1e3,m<=1e4

解法:

分层图最短路

code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e3+5;
const int M=1e5+5;
const int inf=0x3f3f3f3f;
int head[N],nt[M<<1],to[M<<1],w[M<<1],cnt;
int d[N][N];
int n,m,k;
void add(int x,int y,int z){
    cnt++;nt[cnt]=head[x];head[x]=cnt;to[cnt]=y;w[cnt]=z;
}
void dj(){
    typedef pair<int,int> PI;
    priority_queue<PI,vector<PI>,greater<PI> >q;
    for(int i=1;i<=n;i++){
        for(int j=0;j<=k;j++){
            d[i][j]=inf;
        }
    }
    d[1][0]=0;
    q.push({d[1][0],1});
    while(!q.empty()){
        int x=q.top().second;q.pop();
        int c=x/n;//层
        x%=n;//点
        for(int i=head[x];i;i=nt[i]){
            int v=to[i];
            int cost=max(w[i],d[x][c]);
            if(d[v][c]>cost){//走当前层
                d[v][c]=cost;
                q.push({d[v][c],v+c*n});
            }
            if(c<k){//走下一层
                if(d[v][c+1]>d[x][c]){
                    d[v][c+1]=d[x][c];
                    q.push({d[v][c+1],v+(c+1)*n});
                }
            }
        }
    }
}
signed main(){
    cin>>n>>m>>k;
    for(int i=1;i<=m;i++){
        int a,b,c;cin>>a>>b>>c;
        add(a,b,c);add(b,a,c);
    }
    dj();
    int ans=inf;
    for(int i=0;i<=k;i++){
        ans=min(ans,d[n][i]);
    }
    if(ans==inf)ans=-1;
    cout<<ans<<endl;
    return 0;
}

341. 最优贸易

题意:

给n个点m条边的图,保证无自环和重边。
这 m 条道路中有一部分为单向通行的道路,一部分为双向通行的道路,双向通行的道路在统计条数时也计为1条。每个城市出售商品,同一种商品在同一个城市的买入价和卖出价始终是相同的。

商人阿龙来旅游,当他得知“同一种商品在不同城市的价格可能会不同”这一信息之后,便决定在旅游的同时,利用商品在不同城市中的差价赚一点旅费。
设n个城市的标号从 1~n,阿龙决定从1号城市出发,并最终在 n 号城市结束自己的旅行。
在旅游的过程中,任何城市可以被重复经过多次,但不要求经过所有 n 个城市。
阿龙通过这样的贸易方式赚取旅费:他会选择一个经过的城市买入他最喜欢的商品——水晶球,并在之后经过的另一个城市卖出这个水晶球,用赚取的差价当做旅费。

因为阿龙主要是来C国旅游,他决定这个贸易只进行最多一次,当然,在赚不到差价的情况下他就无需进行贸易。

现在给出 n 个城市的水晶球价格,m 条道路的信息(每条道路所连接的两个城市的编号以及该条道路的通行情况)。
请你告诉阿龙,他最多能赚取多少旅费。

解法:

y总的题解:
在这里插入图片描述
为什么不能用dj呢?
在这里插入图片描述

看到另外一种解法:
根据上面题解里面说的,这题主要是环的问题,因此可以先将环缩点,然后图就变成有向无环图了。
接着拓扑排序即可。

ps:
spfa解法:
求1到 i 的dmin[]用的是正向图,求n到 i 的dmax[]是反向图,共两次spfa

缩点+拓扑解法:
同样是双向图,缩点拓扑也都是两次,码量应该会大一点

代码用是spfa解法

code:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
const int M=5e5+5;
vector<int>g[N],gg[N];
int dmi[N],dma[N];
int val[N];
int mark[N];
int n,m;
void spfa(int st,vector<int>g[N],int *d,int f){
    for(int i=1;i<=n;i++){
        d[i]=!f?1e9:0;
        mark[i]=0;
    }
    queue<int>q;
    q.push(st);
    d[st]=val[st];
    mark[st]=1;
    while(!q.empty()){
        int x=q.front();q.pop();
        mark[x]=0;
        for(int v:g[x]){
            if(!f){//mi
                if(d[v]>min(d[x],val[v])){
                    d[v]=min(d[x],val[v]);
                    if(!mark[v]){
                        mark[v]=1;
                        q.push(v);
                    }
                }
            }else{//ma
                if(d[v]<max(d[x],val[v])){
                    d[v]=max(d[x],val[v]);
                    if(!mark[v]){
                        mark[v]=1;
                        q.push(v);
                    }
                }
            }
        }
    }
}
signed main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&val[i]);
    for(int i=1;i<=m;i++){
        int a,b,c;scanf("%d%d%d",&a,&b,&c);
        if(c==1){//单向
            g[a].push_back(b);
            gg[b].push_back(a);
        }else if(c==2){//双向
            g[a].push_back(b);
            g[b].push_back(a);
            gg[a].push_back(b);
            gg[b].push_back(a);
        }
    }
    spfa(1,g,dmi,0);
    spfa(n,gg,dma,1);
    int ans=0;
    for(int i=1;i<=n;i++){
        ans=max(ans,dma[i]-dmi[i]);
    }
    printf("%d\n",ans);
    return 0;
}

342. 道路与航线

题意:

有n个城市,r条双向边,p条单向边,起点为st
边有边权,单向边的边权可能为负数,保证单向边如果是从a到达b,那么不存在从b回到a的路径,即不存在负环。
保证不存在环

问从起点到每个点的最短距离是多少

数据范围:n<=3e4,r,p<=5e4

解法:

显然是一道单源最短路,存在负边权就用spfa。
但是这题会卡spfa

看到了别人的SLF优化:
在这里插入图片描述

ps:
spfa的优化都太玄学了,感觉知道一下就行了

code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+5;
const int M=1e6+6;
const int inf=1e18;
int head[N],nt[M],to[M],w[M],cnt;
int mark[N];
int dist[N];
int n,r,p,s;
void add(int x,int y,int z){
    cnt++;nt[cnt]=head[x];head[x]=cnt;to[cnt]=y;w[cnt]=z;
}
void spfa(int st){
    deque<int>q;
    for(int i=1;i<=n;i++){
        dist[i]=inf;
    }
    dist[st]=0;
    mark[st]=1;
    q.push_back(st);
    while(!q.empty()){
        int x=q.front();q.pop_front();
        mark[x]=0;
        for(int i=head[x];i;i=nt[i]){
            int v=to[i];
            if(dist[v]>dist[x]+w[i]){
                dist[v]=dist[x]+w[i];
                if(!mark[v]){
                    mark[v]=1;
                    if(!q.empty()&&dist[v]<dist[q.front()]){
                        q.push_front(v);
                    }else{
                        q.push_back(v);
                    }
                }
            }
        }
    }
}
signed main(){
    cin>>n>>r>>p>>s;
    for(int i=1;i<=r;i++){//双向
        int a,b,c;cin>>a>>b>>c;
        add(a,b,c);
        add(b,a,c);
    }
    for(int i=1;i<=p;i++){
        int a,b,c;cin>>a>>b>>c;
        add(a,b,c);
    }
    spfa(s);
    for(int i=1;i<=n;i++){
        if(dist[i]==inf){
            puts("NO PATH");
        }else{
            cout<<dist[i]<<endl;
        }
    }
    return 0;
}

343. 排序

题意:

给定 n 个变量和 m 个不等式。其中 n 小于等于26,变量分别用前 n 的大写英文字母表示。
不等式之间具有传递性,即若 A>B 且 B>C ,则 A>C。
请从前往后遍历每对关系,每次遍历时判断:
1.如果能够确定全部关系且无矛盾,则结束循环,输出确定的次序;
2.如果发生矛盾,则结束循环,输出有矛盾;
3.如果循环结束时没有发生上述两种情况,则输出无定解。

数据范围:n<=26,即最多只有A-Z

解法:

观察到数据范围较小,且题目显然是传递关系,因此可以用floyd传递闭包(拓扑排序也可以)
每次新添加一条边都floyd一次,如果存在自环则冲突,否则如果g(i,j)!=g(j,i)对于所有i!=j都成立,则可以确定次序

code:
#include<bits/stdc++.h>
using namespace std;
const int N=105;
bool in[N],vis[N];
bool g[N][N];
int n,m;
void floyd(){//插点
    for(int k=1;k<=n;k++){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                g[i][j]|=(g[i][k]&g[k][j]);
            }
        }
    }
}
int check(){
    for(int i=1;i<=n;i++){//判断是否存在自环,即矛盾
        if(g[i][i])return -1;
    }
    int ok=1;
    for(int i=1;i<=n&&ok;i++){//判断是否能确定关系,即满足g[i][j]!=g[j][i]
        for(int j=1;j<=n&&ok;j++){
            if(i==j)continue;
            if(g[i][j]==g[j][i]){
                ok=0;
            }
        }
    }
    if(ok)return 1;//可以确定
    return 0;//不能确定
}
void print(){//暴力输出入度为0的未标记点
    memset(in,0,sizeof in);
    for(int i=1;i<=n;i++){
        if(vis[i])continue;
        for(int j=1;j<=n;j++){
            if(vis[j])continue;
            if(g[i][j])in[j]=1;
        }
    }
    for(int i=1;i<=n;i++){
        if(vis[i])continue;
        if(!in[i]){
            vis[i]=1;
            printf("%c",i+'A'-1);
        }
    }
}
signed main(){
    while(scanf("%d%d",&n,&m)!=EOF){
        if(!n&&!m)break;
        memset(vis,0,sizeof vis);
        memset(g,0,sizeof g);
        int f=0;
        for(int i=1;i<=m;i++){
            char s[4];scanf("%s",s);
            if(f)continue;//如果已经得出结果则不处理数据
            int a=s[0]-'A'+1,b=s[2]-'A'+1;
            g[a][b]=1;
            floyd();
            f=check();
            if(f==1)f=i;//记录成功位置
            if(f==-1)f=-i;//记录矛盾位置
        }
        if(f<0){//矛盾
            printf("Inconsistency found after %d relations.\n",-f);
        }else if(f==0){//无法确定
            printf("Sorted sequence cannot be determined.\n");
        }else{//可以确定
            printf("Sorted sequence determined after %d relations: ",f);
            for(int i=1;i<=n;i++)print();//输出路径
            puts(".");
        }
    }
    return 0;
}

344. 观光之旅

poj1734 Sightseeing trip


345. 牛站

Poj3613 Cow Relays


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值