题目链接
Description
冷锋在非洲完成任务后回到了狼牙特种作战部队。我们知道在战狼二结尾,冷锋正在北极执行任务,而部队发现了龙小云在c国的消息,让冷锋尽快赶往c国。我们知道现在地球上共有n个国家和地区,编号分别为1,2,3…n。国家与国家之间的可能通航班(可能不止一次),也可能没有通航班。共有m次航班,冷锋已经知道了这m次航班的信息(起点 终点,时间)北极的编号是1,c国的编号是n。
而冷峰身为超级英雄一样的的存在,他有一次将航班的时间降为零的能力。
Input
样例数t(t<=10),接下来又t组样例。 每组样例先输入n , m(n<=1000 , m<=n*(n-1)/2)。
下面m行航班的信息,分别为start , end , time(time <= 100000).
Output
对每组样例,输出冷锋到达c国的最短时间,若不能到达输出 “Impossible”。
Sample Input
3
3 1
1 2 1
4 3
1 2 4
2 3 1
2 4 4
3 3
1 2 100000
2 3 1
1 3 2
Sample Output
Impossible
4
0
真的是不想说,这出题人语文怕是白学了,题目描述路线是航班,还有起点,终点,按理说是有向图,然而这个题竟然是无向图,(鉴于出题人已经修改题面,这里就不吐槽这么狠了。)
思路就是先跑出起点1到其他任意点的距离存到dis2里面,然后再跑一下n到其他任意点的距离存到dis1里面,然后再枚举每条边,判断把这条边的权值置为0,是否能让结果更小,由于是无向图,所以对于u-v这条边,res = min(res,dis1[u]+dis2[v],dis1[v]+dis2[u]);
这题还是非常好的,开阔思路,如果这是有向图的话,我们的做法就是同样先跑出1到其他任意点的距离存到dis2里面,然后再把边反向存一下,再跑出任意一个点到n的距离,然后再枚举每条u-v边,即
res = min(res,dis2[u]+dis1[v]);
题目代码如下:
#include
using namespace std;
const int MAX_V = 1010;
const int MAX_E = 500010;
const int INF = 0x3f3f3f3f;
int x[MAX_E],y[MAX_E],z[MAX_E];
int dis1[MAX_V],dis2[MAX_V];
bool used[MAX_V];
struct Edge{
int to;
int cost;
}es;
vector G[MAX_V];
int n,m;
void spfa(int a_){
fill(dis1+1,dis1+1+n,INF);
memset(used,false,sizeof(used));
dis1[a_] = 0;
queue<int> q;
q.push(a_);used[a_] = true;
while(!q.empty()){
int k = q.front();q.pop();
int len = G[k].size();
for(int i=0;iif(dis1[es.to] > dis1[k] + es.cost){
dis1[es.to] = dis1[k] + es.cost;
if(!used[es.to]){
q.push(es.to);
}
}
}
used[k] = false;
}
}
int main(void){
int t;
scanf("%d",&t);
while(t--){
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
G[i].clear();
for(int i=1;i<=m;i++){
scanf("%d %d %d",&x[i],&y[i],&z[i]);
es.to = y[i];es.cost = z[i];
G[x[i]].push_back(es);
es.to = x[i];
G[y[i]].push_back(es);
}
spfa(1);
for(int i=1;i<=n;i++){
dis2[i] = dis1[i];
}
spfa(n);
int res = INF;
for(int i=1;i<=m;i++){
res = min(res,dis2[x[i]]+dis1[y[i]]);
res = min(res,dis2[y[i]]+dis1[x[i]]);
}
if(res == INF)
printf("Impossible\n");
else
printf("%d\n",res);
}
return 0;
}
过了将近一年,发现这个题目应该是一道裸的分层图,不过这里是特殊情况,只有将一条边变为0,如果是将不超过K边变为0的题,就是一道标准的分层图题目了,做法也很简单,就是建立一个K层的图,没层的连接是原来的边,不过这里权值为0
下面是我用分层图做法的代码,因为这里K=1,所以只需要建立两层就可以了。
代码如下:
#include
using namespace std;
typedef long long ll;
const int MAX_V = 1010;
const int MAX_E = 500010;
const ll INF = 1e16+10;
class Edge{
public:
int to,nex;
ll cost;
Edge();
Edge(int _to,int _nex,ll _cost);
};
Edge edg[10*MAX_E];
int head[3*MAX_V],cnt;
ll dis[3*MAX_V];
void init(){
memset(head,-1,sizeof(head));
cnt = 0;
}
void add_Edge(int u,int v,ll w){
edg[cnt] = Edge(v,head[u],w);
head[u] = cnt++;
}
int N,M;
class Rule{
public:
bool operator() (const int A,const int B){
return dis[A] > dis[B];
}
};
void Dijkstra(int s,int t){
for(int i=0;i<=2*N;++i) dis[i] = INF;
priority_queue<int,vector<int>,Rule > que;
dis[s] = 0;
que.push(s);
while(!que.empty()){
int u = que.top();que.pop();
for(int i=head[u];~i;i=edg[i].nex){
Edge &e = edg[i];
if(dis[e.to] > dis[u] + e.cost){
dis[e.to] = dis[u] + e.cost;
que.push(e.to);
}
}
}
ll res = min(dis[N],dis[2*N]);
if(res == INF){
printf("Impossible\n");
}
else{
printf("%lld\n",res);
}
}
int main(void){
int T;
scanf("%d",&T);
while(T--){
init();
scanf("%d%d",&N,&M);
int u,v;
ll w;
for(int i=1;i<=M;++i){
scanf("%d%d%lld",&u,&v,&w);
add_Edge(u,v,w);
add_Edge(v,u,w);
add_Edge(u+N,v+N,w);
add_Edge(v+N,u+N,w);
add_Edge(u,v+N,0);
add_Edge(v,u+N,0);
}
Dijkstra(1,N);
}
return 0;
}
Edge::Edge(){
to = nex = 0;
cost = 0;
}
Edge::Edge(int _to,int _nex,ll _cost){
to = _to;
nex = _nex;
cost = _cost;
}
对于多层的情况,可以看下洛谷 2939