20211123 HDU练习 最短路和最小生成树

一些模板题,都是dijkstra和prim

第一次写博客就复制黏贴实验报告非常抱歉,下次一定好好写。()

以后自己做的题也会写点题解吧  欢迎找我玩~

HDU Today

Time Limit : 15000/5000ms (Java/Other)   Memory Limit : 32768/32768K (Java/Other)

Total Submission(s) : 40   Accepted Submission(s) : 2

Font: Times New Roman | Verdana | Georgia

Font Size: ← →

Problem Description

经过锦囊相助,海东集团终于度过了危机,从此,HDU的发展就一直顺风顺水,到了2050年,集团已经相当规模了,据说进入了钱江肉丝经济开发区500强。这时候,XHD夫妇也退居了二线,并在风景秀美的诸暨市浬浦镇陶姚村买了个房子,开始安度晚年了。
这样住了一段时间,徐总对当地的交通还是不太了解。有时很郁闷,想去一个地方又不知道应该乘什么公交车,在什么地方转车,在什么地方下车(其实徐总自己有车,却一定要与民同乐,这就是徐总的性格)。
徐总经常会问蹩脚的英文问路:“Can you help me?”。看着他那迷茫而又无助的眼神,热心的你能帮帮他吗?
请帮助他用最短的时间到达目的地(假设每一路公交车都只在起点站和终点站停,而且随时都会开)。

Input

输入数据有多组,每组的第一行是公交车的总数N(0<=N<=10000)
第二行有徐总的所在地start,他的目的地end
接着有n行,每行有站名s,站名e,以及从se的时间整数t(0<t<100)(每个地名是一个长度不超过30的字符串)
note:一组数据中地名数不会超过150个。
如果N==-1,表示输入结束。

Output

如果徐总能到达目的地,输出最短的时间;否则,输出“-1”

Sample Input

6

xiasha westlake

xiasha station 60

xiasha ShoppingCenterofHangZhou 30

station westlake 20

ShoppingCenterofHangZhou supermarket 10

xiasha supermarket 50

supermarket westlake 10

-1

Sample Output

题目的分析

最短路径问题,难点在每个节点的输入不是编号而是字符串形式的地名。

以及没给样例输出

解决方法:用一个map<string,int> ,以地名为键值编号就好啦。STL真好用!

用了优先队列优化的dijkstra

代码

#include<bits/stdc++.h>

using namespace std;



#define INF 0x3f3f3f3f



int n;

int graph[155][155];

string start,endd;

int dis[105];

bool vis[105];



int main(){

    int t,cnt,i;

    string s,e;

    map<string,int> mapp;

    while(1){

        cin >> n;

        if(n==-1)   break;

        cin >> start >> endd;

        mapp.clear();

        memset(graph,INF,sizeof(graph));

        cnt=1;

        mapp[start]=1;

        if(!mapp[endd])    mapp[endd]=++cnt;

        while(n--){

            cin >> s >> e >> t;

            if(!mapp[s])    mapp[s]=++cnt;

            if(!mapp[e])    mapp[e]=++cnt;

            //cout << s << " " << mapp[s] << endl;

            //cout << e << " " << mapp[e] << endl;

            graph[mapp[s]][mapp[e]]=graph[mapp[e]][mapp[s]]=t;

        }

        memset(vis,false,sizeof(vis));

        memset(dis,INF,sizeof(dis));

        vis[0]=true;

        dis[1]=0;

        priority_queue<pair<int,int>> q;    //(-长度,结点编号)

        for(i=2;i<=n;i++){

            dis[i]=graph[1][i];

            if(dis[i]!=INF){

                q.push(make_pair(-dis[i],i));

            }

        }

        q.push(make_pair(-0,1));

        while(!vis[mapp[endd]]){

            i=0;

            while(vis[i]){

                i=q.top().second;

                q.pop();                

            }

            vis[i]=true;

            for(int j=1;j<=cnt;j++){

                if(!vis[j] && dis[j]>dis[i]+graph[i][j]){

                    dis[j]=dis[i]+graph[i][j];

                    q.push(make_pair(-dis[j],j));

                }

            }

        }

        if(dis[mapp[endd]]!=INF) cout << dis[mapp[endd]];

        else    cout << "-1";

        cout << endl;

    }

    return 0;

}

代码运行情况

美美过了

感想

在做最短路径/最小生成树问题的时候,需要有的一些习惯:

  1. 是否有重边?
  2. 是否有负值边/负值圈?(选择不同算法)
  3. 是否需要输出路径,还是只需要计算路径长度?
  4. 根据图是稀疏或稠密选择算法(暂时没遇到卡得很严格的题)
  5. 优化一下总是没错的,多用scanf少用cin总是没错的

畅通工程

Time Limit : 1000/1000ms (Java/Other)   Memory Limit : 32768/32768K (Java/Other)

Total Submission(s) : 90   Accepted Submission(s) : 20

Font: Times New Roman | Verdana | Georgia

Font Size: ← →

Problem Description

省政府畅通工程的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可)。经过调查评估,得到的统计表中列出了有可能建设公路的若干条道路的成本。现请你编写程序,计算出全省畅通需要的最低成本。

Input

测试输入包含若干测试用例。每个测试用例的第1行给出评估的道路条数 N、村庄数目M ( < 100 );随后的 N
行对应村庄间道路的成本,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间道路的成本(也是正整数)。为简单起见,村庄从1M编号。当N0时,全部输入结束,相应的结果不要输出。

Output

对每个测试用例,在1行里输出全省畅通需要的最低成本。若统计数据不足以保证畅通,则输出“?”

Sample Input

3 3
1 2 1
1 3 2
2 3 4
1 3
2 3 2
0 100

Sample Output

3
?

Source

浙大计算机研究生复试上机考试-2007

题目的分析

最小生成树问题。用prim算法,因为不确定N的大小,还是不用kruskal冒险了(其实是前一题写了dijkstra好改

代码

#include<bits/stdc++.h>

using namespace std;



#define INF 0x3f3f3f3f



int main(){

    int n,m,i,j,k,visted,ans;

    int w[105][105];

    int dis[105];

    bool vis[105];

    while(1){

        cin >> n >> m;

        if(n==0)    break;

        memset(w,INF,sizeof(w));

        while(n--){

            cin >> i >> j >> k;

            if(w[j][i]>k)   w[i][j]=w[j][i]=k;

        }

        ans=visted=0;

        memset(dis,INF,sizeof(dis));

        memset(vis,false,sizeof(vis));

        priority_queue<pair<int,int>> q;

        dis[1]=0;   vis[0]=true;

        for(i=2;i<=m;i++){

            dis[i]=w[1][i];

            if(dis[i]!=INF){

                q.push(make_pair(-dis[i],i));

            }

        }

        q.push(make_pair(0,1));

        while(!q.empty()){

            i=0;

            while(vis[i] && (!q.empty())){

                i=q.top().second;

                q.pop();

            }

            if(i==0)    break;

            vis[i]=true;

            visted++;

            ans+=dis[i];

            for(j=2;j<=m;j++){

                if((!vis[j]) && dis[j]>w[i][j]){

                    dis[j]=w[i][j];

                    q.push(make_pair(-dis[j],j));

                }

            }

        }

        if(visted==m)   cout << ans << endl;

        else cout << "?" << endl;



    }

    return 0;

}

代码运行情况

美美过了

感想

对Kruskal的应用场景产生怀疑,决定找点相关题目做做。

还是畅通工程

Time Limit : 4000/2000ms (Java/Other)   Memory Limit : 65536/32768K (Java/Other)

Total Submission(s) : 40   Accepted Submission(s) : 15

Font: Times New Roman | Verdana | Georgia

Font Size: ← →

Problem Description

某省调查乡村交通状况,得到的统计表中列出了任意两村庄间的距离。省政府畅通工程的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可),并要求铺设的公路总长度为最小。请计算最小的公路总长度。

Input

测试输入包含若干测试用例。每个测试用例的第1行给出村庄数目N ( < 100 );随后的N(N-1)/2行对应村庄间的距离,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间的距离。为简单起见,村庄从1N编号。
N0时,输入结束,该用例不被处理。

Output

对每个测试用例,在1行里输出最小的公路总长度。

Sample Input

3
1 2 1
1 3 2
2 3 4
4
1 2 1
1 3 4
1 4 1
2 3 3
2 4 2
3 4 5
0

Sample Output

3
5

Hint

Hint
Huge input, scanf is recommended.

Source

浙大计算机研究生复试上机考试-2006

题目的分析

还是最小生成树问题。这次说了是稠密图,可能意思是1002要写kruskal吧     TT。

用之前的代码改了一下,还好今天的题目都没有负权边。

注意循环的退出条件,在所有顶点都访问过(visted==m )的时候就可以退出了。

代码

#include<bits/stdc++.h>

using namespace std;



#define INF 0x3f3f3f3f



int main(){

    int n,m,i,j,k,visted,ans;

    int w[105][105];

    int dis[105];

    bool vis[105];

    while(1){

        cin >> m;

        n=m*(m-1)/2;

        if(m==0)    break;

        memset(w,INF,sizeof(w));

        while(n--){

            //cin >> i >> j >> k;

            scanf("%d%d%d",&i,&j,&k);

            if(w[j][i]>k)   w[i][j]=w[j][i]=k;

        }

        ans=visted=0;

        memset(dis,INF,sizeof(dis));

        memset(vis,false,sizeof(vis));

        priority_queue<pair<int,int>> q;

        dis[1]=0;   vis[0]=true;

        for(i=2;i<=m;i++){

            dis[i]=w[1][i];

            if(dis[i]!=INF){

                q.push(make_pair(-dis[i],i));

            }

        }

        q.push(make_pair(0,1));

        while(!q.empty()){

            i=0;

            while(vis[i] && (!q.empty())){

                i=q.top().second;

                q.pop();

            }

            if(i==0 || visted==m)   break;

            vis[i]=true;

            visted++;

            ans+=dis[i];

            for(j=2;j<=m;j++){

                if((!vis[j]) && dis[j]>w[i][j]){

                    dis[j]=w[i][j];

                    q.push(make_pair(-dis[j],j));

                }

            }

        }

        /*

        cout << visted << endl;

        if(visted==m)   cout << ans << endl;

        else cout << "?" << endl;

        */

        cout << ans << endl;

    }

    return 0;

}

代码运行情况

美美过了

感想

此处空白

最短路

Time Limit : 5000/1000ms (Java/Other)   Memory Limit : 32768/32768K (Java/Other)

Total Submission(s) : 114   Accepted Submission(s) : 30

Font: Times New Roman | Verdana | Georgia

Font Size: ← →

Problem Description

在每年的校赛里,所有进入决赛的同学都会获得一件很漂亮的t-shirt。但是每当我们的工作人员把上百件的衣服从商店运回到赛场的时候,却是非常累的!所以现在他们想要寻找最短的从商店到赛场的路线,你可以帮助他们吗?

Input

输入包括多组数据。每组数据第一行是两个整数NMN<=100M<=10000),N表示成都的大街上有几个路口,标号为1的路口是商店所在地,标号为N的路口是赛场所在地,M则表示在成都有几条路。N=M=0表示输入结束。接下来M行,每行包括3个整数ABC1<=A,B<=N,1<=C<=1000,表示在路口A与路口B之间有一条路,我们的工作人员需要C分钟的时间走过这条路。
输入保证至少存在1条商店到赛场的路线。

Output

对于每组输入,输出一行,表示工作人员从商店走到赛场的最短时间

Sample Input

2 1
1 2 3
3 3
1 2 5
2 3 5
3 1 2
0 0

Sample Output

3
2
 
 

Source

UESTC 6th Programming Contest Online

题目的分析

最短路径,dijkstra。

代码

#include<bits/stdc++.h>

using namespace std;



#define INF 0x3f3f3f3f



int main(){

    int n,m,i,j,k,visted;

    int w[105][105];

    int dis[105];

    bool vis[105];

    while(1){

        //cin >> n >> m;

        cin >> m >> n;

        if(n==0 && m==0)    break;

        memset(w,INF,sizeof(w));

        while(n--){

            cin >> i >> j >> k;

            if(w[j][i]>k)   w[i][j]=w[j][i]=k;

        }

        visted=0;

        memset(dis,INF,sizeof(dis));

        memset(vis,false,sizeof(vis));

        vis[1]=true;

        dis[1]=0;

        for(i=2;i<=m;i++){

            dis[i]=w[1][i];

        }

        for(i=2;i<=m;i++){

            int min,pos;

            min=INF;

            for(j=2;j<=m;j++){

                if((!vis[j]) && dis[j]<min){

                    min=dis[j];

                    pos=j;

                }

            }

            if(min==INF)    break;

            vis[pos]=true;

            if(vis[m])  break;

            for(j=2;j<=m;j++){

                if((!vis[j]) && dis[j]>dis[pos]+w[pos][j]){

                    dis[j]=dis[pos]+w[pos][j];

                }

            }            

        }

        cout << dis[m] << endl;

    }

    return 0;

}

代码运行情况

美美过了

感想

此处空白

最短路径问题

Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 32768/32768K (Java/Other)

Total Submission(s) : 25   Accepted Submission(s) : 1

Font: Times New Roman | Verdana | Georgia

Font Size: ← →

Problem Description

给你n个点,m条无向边,每条边都有长度d和花费p,给你起点s终点t,要求输出起点到终点的最短距离及其花费,如果最短距离有多条路线,则输出花费最少的。

Input

输入n,m,点的编号是1~n,然后是m行,每行4个数 a,b,d,p,表示ab之间有一条边,且其长度为d,花费为p。最后一行是两个数 s,t;起点s,终点。nm0时输入结束。
(1<n<=1000, 0<m<100000, s != t)

Output

输出 一行有两个数, 最短距离及其花费。

Sample Input

3 2
1 2 5 6
2 3 4 5
1 3
0 0

Sample Output

9 11

Source

浙大计算机研究生复试上机考试-2010

题目的分析

最短路径,dijkstra!好像有同学写floyd,那是必定要超时的。

“长度d和花费p”其实就是主次关键字,在更新dis[]和寻找下一个访问的顶点时注意更新条件就可以了。

然后起点终点并不默认是1和n,复制黏贴的时候要注意

代码

#include<bits/stdc++.h>

using namespace std;



#define INF 0x3f3f3f3f



int n,m,i,j,d,p,visted,s,t;

int w[1005][1005];

int mm[1005][1005];

int dis[1005],cost[1005];

bool vis[1005];



int main(){

    while(1){

        //cin >> n >> m;

        cin >> m >> n;

        if(n==0 && m==0)    break;

        memset(w,INF,sizeof(w));

        memset(mm,INF,sizeof(m));

        while(n--){

            //cin >> i >> j >> d >> p;

            scanf("%d%d%d%d",&i,&j,&d,&p);

            if((w[j][i]>d) || (w[j][i]==d && mm[j][i]>p)){

                w[i][j]=w[j][i]=d;

                mm[i][j]=mm[j][i]=p;

            }

        }

        scanf("%d%d",&s,&t);

        visted=0;

        memset(dis,INF,sizeof(dis));

        memset(vis,false,sizeof(vis));

        memset(cost,INF,sizeof(cost));

        for(i=1;i<=m;i++){

            dis[i]=w[s][i];

            cost[i]=mm[s][i];

        }

        vis[s]=true;

        dis[s]=0;

        cost[s]=0;

        for(i=1;i<=m;i++){

            int min,pos,mincost;

            mincost=min=INF;

            for(j=1;j<=m;j++){

                if((!vis[j]) && (dis[j]<min || (dis[j]==min && cost[j]<mincost))){

                    min=dis[j];

                    mincost=cost[j];

                    pos=j;

                }

            }

            if(min==INF)    break;

            vis[pos]=true;

            if(vis[t])  break;

            for(j=1;j<=m;j++){

                if((!vis[j]) && (dis[j]>dis[pos]+w[pos][j] || (dis[j]==dis[pos]+w[pos][j] && cost[j]>cost[pos]+mm[pos][j]))){

                    dis[j]=dis[pos]+w[pos][j];

                    cost[j]=cost[pos]+mm[pos][j];

                }

            }            

        }

        cout << dis[t] << " " << cost[t] << endl;

    }

    return 0;

}

代码运行情况

美美过了

感想

细心细心再细心。

最好还是给dijkstra单独写个函数,条件判断单独写一个函数吧,这次全放一起能看明白,下次题目更复杂就不一定了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值