L2-001 紧急救援 最短路+路径打印
作为一个城市的应急救援队伍的负责人,你有一张特殊的全国地图。在地图上显示有多个分散的城市和一些连接城市的快速道路。每个城市的救援队数量和每一条连接两个城市的快速道路长度都标在地图上。当其他城市有紧急求助电话给你的时候,你的任务是带领你的救援队尽快赶往事发地,同时,一路上召集尽可能多的救援队。
输入格式:
输入第一行给出4个正整数N、M、S、D,其中N(2≤N≤500)是城市的个数,顺便假设城市的编号为0 ~ (N−1);M是快速道路的条数;S是出发地的城市编号;D是目的地的城市编号。
第二行给出N个正整数,其中第i个数是第i个城市的救援队的数目,数字间以空格分隔。随后的M行中,每行给出一条快速道路的信息,分别是:城市1、城市2、快速道路的长度,中间用空格分开,数字均为整数且不超过500。输入保证救援可行且最优解唯一。
输出格式:
第一行输出最短路径的条数和能够召集的最多的救援队数量。第二行输出从S到D的路径中经过的城市编号。数字间以空格分隔,输出结尾不能有多余空格。
输入样例:
4 5 0 3
20 30 40 10
0 1 1
1 3 2
0 3 3
0 2 2
2 3 2
输出样例:
2 60
0 1 3
分析:
Dijkstra基础应用,比模板单纯求最短路的基础上多了输出路径,路径条数以及多权重(路径相同时人数尽量大),路径只要存每个节点的前驱,然后倒着遍历一遍就行。其他就是一些小细节,看注释。优先队列默认为大顶堆priority_queue<PII>
,这里需要改成小顶堆priority_queue<PII,vector<PII>,greater<PII> >
代码:
#include<bits/stdc++.h>
using namespace std;
#define PII pair<int,int>const int INF = 0x3f3f3f3f;
const int N = 510;//n个点m条边从s到d
int n,m,s,d;
//分别为:邻接矩阵存图,是否访问过,到起点的最短距离。
vector<PII> mp[N];
int vis[N], dis[N];
//分别为:到当前点最多召集几个人,每个城市的人数,存路径,到当前点几条路。
int tot[510], city[510], path[510], road[510];void DJ(int s){//初始化 for(int i = 1 ; i <= n ; i ++ ) dis[i] = INF, vis[i] = false, path[s] = -1;tot[s] = city[s];dis[s] = 0;road[s] = 1;//小顶堆 priority_queue<PII, vector<PII>, greater<PII> > q; q.push({0, s}); while(q.size()){int t = q.top().second;q.pop();if(vis[t]) continue;vis[t] = true;for(int i = 0 ; i < mp[t].size() ; i ++ ){int j = mp[t][i].first, w = mp[t][i].second;if(dis[j] > dis[t]+w){ //松弛操作 //j从t这个点过来的路径更短,前驱变为tpath[j] = t;//从t走过来,则可以召集的人数就是到t可召集的人数+j这个点的人数tot[j] = tot[t]+city[j];dis[j] = dis[t]+w;//到t有几种走法,就到j有几种road[j] = road[t];//由于j点最短距离被更新,需要压入队列q.push({dis[j], j});}else if(dis[j] == dis[t]+w){//有另外的走法使得最短路相同,更新方案数road[j] += road[t];//如果可以召集更多的人,更新方案if (tot[j] < tot[t] + city[j]) {tot[j] = tot[t] + city[j];path[j] = t;}}}}
}void print(int d){ //路径打印 int now = d;vector<int> v;while(now != -1){v.push_back(now);now = path[now];}for (int i = v.size()-1 ; i >= 0 ; i -- )printf("%d%c", v[i], i==0 ? '\n' : ' ');
}int main(){cin>>n>>m>>s>>d;for(int i = 0 ; i < n ; i ++ ) cin>>city[i]; for(int i = 0 ; i < m ; i ++ ){int x, y, w;cin>>x>>y>>w;mp[x].push_back({y, w});mp[y].push_back({x, w});}DJ(s);cout<<road[d]<<" "<<tot[d]<<endl;print(d);return 0;
}
L2-002 链表去重 模拟链表
给定一个带整数键值的链表 L,你需要把其中绝对值重复的键值结点删掉。即对每个键值 K,只有第一个绝对值等于 K 的结点被保留。同时,所有被删除的结点须被保存在另一个链表上。例如给定 L 为 21→-15→-15→-7→15,你需要输出去重后的链表 21→-15→-7,还有被删除的链表 -15→15。
输入格式:
输入在第一行给出 L 的第一个结点的地址和一个正整数 N(≤105,为结点总数)。一个结点的地址是非负的 5 位整数,空地址 NULL 用 −1 来表示。
随后 N 行,每行按以下格式描述一个结点:
地址 键值 下一个结点
其中地址
是该结点的地址,键值
是绝对值不超过104的整数,下一个结点
是下个结点的地址。
输出格式:
首先输出去重后的链表,然后输出被删除的链表。每个结点占一行,按输入的格式输出。
输入样例:
00100 5
99999 -7 87654
23854 -15 00000
87654 15 -1
00000 -15 99999
00100 21 23854
输出样例:
00100 21 23854
23854 -15 99999
99999 -7 -1
00000 -15 87654
87654 15 -1
分析:
数据量不大,可以直接用数组模拟链表,地址当做数组下标,考察基本的删除操作和尾插法。
代码:
#include<bits/stdc++.h>
using namespace std;const int N=1e5+5;
typedef long long ll;struct node{int id,val,next,absval;
}LNode[N];
bool cmp(node a,node b){return a.id<b.id;
}
pair<int,pair<int,int> > p;
vector<pair<int,pair<int,int> > > com,del;
int f[N];int main(){int ID,N,i;cin>>ID>>N;for(i=0;i<N;i++){cin>>LNode[i].id>>LNode[i].val>>LNode[i].next;LNode[i].absval=fabs(LNode[i].val);f[abs(LNode[i].val)]=0; //避免二分时重复计算 }sort(LNode,LNode+N,cmp);while(ID!=-1){ //用-1结束,别用N计数 int l=0,r=N-1; //二分查找下一节点 while(l<r){int mid=(l+r)/2;if(LNode[mid].id<ID) l=mid+1;else r=mid;}p.first=LNode[l].id;p.second.first=LNode[l].val;p.second.second=LNode[l].next;if(f[LNode[l].absval]==0){f[LNode[l].absval]=1;com.push_back(p);}elsedel.push_back(p);ID=LNode[l].next;}for(i=0;i<com.size();i++){if(i<com.size()-1)printf("%05d %d %05d\n",com[i].first,com[i].second.first,com[i+1].first);elseprintf("%05d %d -1\n",com[i].first,com[i].second.first);}for(i=0;i<del.size();i++){if(i<del.size()-1)printf("%05d %d %05d\n",del[i].first,del[i].second.first,del[i+1].first);elseprintf("%5d %d -1\n",del[i].first,del[i].second.first);}return 0;
}