城市平乱
-
描述
-
南将军统领着N个部队,这N个部队分别驻扎在N个不同的城市。
他在用这N个部队维护着M个城市的治安,这M个城市分别编号从1到M。
现在,小工军师告诉南将军,第K号城市发生了暴乱,南将军从各个部队都派遣了一个分队沿最近路去往暴乱城市平乱。
现在已知在任意两个城市之间的路行军所需的时间,你作为南将军麾下最厉害的程序员,请你编写一个程序来告诉南将军第一个分队到达叛乱城市所需的时间。
注意,两个城市之间可能不只一条路。
-
输入
-
第一行输入一个整数T,表示测试数据的组数。(T<20)
每组测试数据的第一行是四个整数N,M,P,Q(1<=N<=100,N<=M<=1000,M-1<=P<=100000)其中N表示部队数,M表示城市数,P表示城市之间的路的条数,Q表示发生暴乱的城市编号。
随后的一行是N个整数,表示部队所在城市的编号。
再之后的P行,每行有三个正整数,a,b,t(1<=a,b<=M,1<=t<=100),表示a,b之间的路如果行军需要用时为t
数据保证暴乱的城市是可达的。
输出
- 对于每组测试数据,输出第一支部队到达叛乱城市时的时间。每组输出占一行 样例输入
-
1 3 8 9 8 1 2 3 1 2 1 2 3 2 1 4 2 2 5 3 3 6 2 4 7 1 5 7 3 5 8 2 6 8 2
样例输出
-
4
来源
首先要解释下:INF=0xffffffff,
我擦,真伤心啊,花费了整整一个星期,终于搞明白了Bellman-ford,dijkstra及其用堆的优化,传说中的SPFA算法,可是谁知道一运行,尼玛bellman -48ms, dijkstra堆优化 -16ms, SPFA - 16ms, 有位大神的竟然SPFA是0ms, dijkstra是4ms, bellman是16ms,,,,,这也差太多了吧,说多了都是泪啊,,,,
由于本人以为这个为最大值,所以整整耗了两天的功夫,想着就可惜,现在总结下;
因为int 为带符号类型,带符号类型最高为是符号位,又因为0xFFFFFFFF,也就是四个字节32 bits全是1, 符号位是1,所以这个数是负数。
内存中的数值为补码表示,所以0xFFFFFFFF是一个负数的补码。负数从补码求原码,最高符号位不变,保持 1, 其余各位求反,末尾加1,也就是 0xFFFFFFFF,二进制为:11111111 11111111 11111111 11111111
-> 10000000 00000000 00000000 00000000
-> 10000000 00000000 00000000 00000001
原码首位表示符号位,其余位表示绝对值大小,所以,这个数是 -1
所以以后要注意了
0x3f3f3f3f == 1061109567;
0x7fffffff == 2147483647;0xffffffff == -1
解法一:Bellman-Ford算法 48ms(可以判断负圈)
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
#include<string.h>
using namespace std;
const int inf=0x3f3f3f3f;
struct Node
{
int from,to,cost;
}es[200010];
int ll,dis[1005],gound[101],prev[1005];
/*vector<int> get_path(int t) //路径还原
{
vector<int> path;
for(;t!=-1;t=prev[t])path.push_back(t);
reverse(path.begin(),path.end());
return path;
}*/
int main()
{
int test;
scanf("%d",&test);
while(test--){
int N,M,P,Q,T=0;
scanf("%d%d%d%d",&N,&M,&P,&Q);
for(int i=0;i<N;i++)scanf("%d",&gound[i]);
for(int i=1;i<=P;i++){ //创建无向图
scanf("%d%d%d",&es[i].from,&es[i].to,&es[i].cost);
es[P+i].from=es[i].to;
es[P+i].to=es[i].from;
es[P+i].cost=es[i].cost;
}
for(int i=0;i<=M;i++){dis[i]=inf;prev[i]=-1;}
dis[Q]=0;
while(true){
bool update=false;
for(int i=1; i<=2*P;i++){
Node e=es[i];
if(dis[e.from]!=inf && dis[e.to]>e.cost+dis[e.from]){
dis[e.to]=e.cost+dis[e.from];
prev[e.to]=e.from; //进行路径还原,查找出最短的路径经过那几个点。
update=true;
}
}
if(!update)break;
}
for(int j=0;j<M;j++) //判断负圈
for(int i=1;i<=2*P;i++){
Node e=es[i];
if(dis[e.from]!=inf && dis[e.to]>e.cost+dis[e.from]){
dis[e.to]=e.cost+dis[e.from];
if(i==M-1)return 0; //当循环第M-1次还进行更新最短路径,说明存在负环
}
}
int res=inf;
for(int i=0;i<N;i++)
if(res>dis[gound[i]])res=dis[gound[i]];
printf("%d\n",res);
}
return 0;
}
解法二:Dijkstra用堆优化 16ms
#include<cstdio>
#include<iostream>
#include<vector>
#include<queue>
#include<algorithm>
#include <cstdlib>
using namespace std;
const int INF=0x3f3f3f3f;
struct Node{int to,cost;};
typedef pair<int,int> A;
vector<Node> G[200010];
int d[1010],gound[105];
int main()
{
int test;
scanf("%d",&test);
while(test--){
int N,M,P,Q;
scanf("%d%d%d%d",&N,&M,&P,&Q);
memset(G,0,sizeof(G));
for(int i=0;i<=M;i++){d[i]=INF;}
d[Q]=0;
for(int i=0;i<N;i++)scanf("%d",&gound[i]);
for(int i=1;i<=P;i++){ //建立邻接表的无向图
int f,t,c;
scanf("%d%d%d",&f,&t,&c);
Node e;
e.to=f; e.cost=c;
G[t].push_back(e);
e.to=t; e.cost=c;
G[f].push_back(e);
}
priority_queue<A,vector<A>,greater<A> > que;
que.push(A(0,Q));
while(!que.empty()){
A p=que.top();que.pop();
int v=p.second;
if(d[v]<p.first)continue;
for(int i=0;i<G[v].size();i++){
Node e=G[v][i];
if(d[e.to]> d[v] + e.cost){
d[e.to]=d[v]+e.cost;
que.push(A(d[e.to],e.to));
}
}
}
int res=INF;
for(int i=0;i<N;i++)
if(res>d[gound[i]])res=d[gound[i]];
printf("%d\n",res);
}
return 0;
}
解法三:SPFA 16ms
(1)用邻接矩阵存储数据
#include<cstdio>
#include<iostream>
#include<vector>
#include<queue>
#include<string.h>
#include<algorithm>
#include <cstdlib>
using namespace std;
const int INF=0x3f3f3f3f;
int d[1010],gound[105],used[1010],map[1010][1010];
int main()
{
int test;
scanf("%d",&test);
while(test--){
int N,V,P,Q;
scanf("%d%d%d%d",&N,&V,&P,&Q);
memset(map,INF,sizeof(map));
for(int i=0;i<N;i++)scanf("%d",&gound[i]);
for(int i=1;i<=P;i++){ //建立邻接矩阵的无向图
int f,t,c;
scanf("%d%d%d",&f,&t,&c);
map[f][t] = map[t][f] = c;
}
for(int i = 1; i <= V; i++){d[i] = INF; used[i]=false;}
queue<int> q;
d[Q] = 0;
used[Q]=true;
q.push(Q);
while(!q.empty()){
int v = q.front(); q.pop();
for(int i = 1; i <= V; i++){
if(d[i] > d[v] + map[v][i]){
d[i] = d[v] + map[v][i];
if(!used[i]){
q.push(i);
used[i] = true;
}
}
}
used[v] = false;
}
int res=INF;
for(int i=0;i<N;i++)
if(res>d[gound[i]])res=d[gound[i]];
printf("%d\n",res);
}
return 0;
}
(2) 用邻接表存储数据(没有发现这种比上一种快多少)
#include<cstdio>
#include<iostream>
#include<vector>
#include<queue>
#include<string.h>
#include<algorithm>
#include <cstdlib>
using namespace std;
const int INF=0x3f3f3f3f;
struct Node{int to,cost;};
vector<Node> G[200010];
queue<int> q;
int d[1010],gound[105],used[1010];
int main()
{
int test;
scanf("%d",&test);
while(test--){
int N,M,P,Q;
scanf("%d%d%d%d",&N,&M,&P,&Q);
memset(G,0,sizeof(G));
for(int i=0;i<=M;i++){d[i]=INF;used[i]=false;}
d[Q]=0;
for(int i=0;i<N;i++)scanf("%d",&gound[i]);
for(int i=1;i<=P;i++){ //建立邻接表的无向图
int f,t,c;
scanf("%d%d%d",&f,&t,&c);
Node e;
e.to=f; e.cost=c;
G[t].push_back(e);
e.to=t; e.cost=c;
G[f].push_back(e);
}
d[Q] = 0;
used[Q]=true;
q.push(Q);
while(!q.empty()){
int v = q.front(); q.pop();
for(int i=0;i<G[v].size();i++){
Node e = G[v][i];
if(d[e.to] > d[v] + e.cost){
d[e.to] = d[v] + e.cost;
if(!used[e.to]){
q.push(e.to);
used[e.to] = true;
}
}
}
used[v] = false;
}
int res=INF;
for(int i=0;i<N;i++)
if(res>d[gound[i]])res=d[gound[i]];
printf("%d\n",res);
}
return 0;
}
-
第一行输入一个整数T,表示测试数据的组数。(T<20)