HDU4725建图题(第一次写博客)!!!

HDU4725:题目

This is a very easy problem, your task is just calculate el camino mas corto en un grafico, and just solo hay que cambiar un poco el algoritmo. If you do not understand a word of this paragraph, just move on. The Nya graph is an undirected graph with “layers”. Each node in the graph belongs to a layer, there are N nodes in total. You can move from any node in layer x to any node in layer x + 1, with cost C, since the roads are bi-directional, moving from layer x + 1 to layer x is also allowed with the same cost. Besides, there are M extra edges, each connecting a pair of node u and v, with cost w. Help us calculate the shortest path from node 1 to node N.
The first line has a number T (T <= 20) , indicating the number of test cases.

input:
For each test case, first line has three numbers N, M (0 <= N, M <= 10^5) and C(1 <= C <= 10^3), which is the number of nodes, the number of extra edges and cost of moving between adjacent layers. The second line has N numbers l i (1 <= l i <= N), which is the layer of i th node belong to. Then come N lines each with 3 numbers, u, v (1 <= u, v < =N, u !=v) and w (1<= w <= 10^4), which means there is an extra edge, connecting a pair of node u and v, with cost w.

Output
For test case X, output "Case #X: " first, then output the minimum cost moving from node 1 to node N.
If there are no solutions, output -1.

Sample Input
2
3 3 3
1 3 2
1 2 1
2 3 1
1 3 3

3 3 3
1 3 2
1 2 2
2 3 2
1 3 4

Sample Output
Case #1: 2
Case #2: 3

题目大意是给出N个点(编号 1 到 N)和M条双向有权边,每条边连接着两个点,表示路径长度;每个点还有自己的一个层值,该点属于第几层;除了已给出的边可走之外,相邻两层可以从x层任意一点跨越到x+1层或x-1层任意一点,且花费为C(不是跨层就要花费C,而是选择跨层而不走已有连接路径时要花费C),问1号点到N号点的最短路。

分析:既然是最短路问题,看到数据规模这题跑最短路显然要用堆优化的dijkstra或SPFA(我选择了更熟悉的SPFA)。相邻层可跨越相当于对两个相邻层次点多了一条可行路径其花费为C。为保证最短路算法的正确性,一定要将所有的可行边都加入松弛考虑的范围,也就是建图。这题的难点在于如何建图 (起初我想的是所有相邻层的点都互相建一条双向边,但这样明显不可行,要建的边数太多算法跑不动)。

解法如下:
如果每两个相邻层的点都建双向边,考虑一种简单的情况,n/2个点位于x层而另外n/2位于x+1层,这种情况要建出 n^2/2 条边.
正确打开方式(从网上学的):把所有层次抽象为一个过渡点,编号N+1到N+i(这里假设i层,实际上可能是给出N层)这样层次就被抽象为一个点,由相邻层的点可到达该层,再通过该层可去下一个相邻层或本层所有其他点( 看到这应该就明白要如何建边了 )。建边:点对点建边,点对相邻层建边,相邻层之间建边,
相邻层到本层点建边。

贴出代码:

#include<iostream>
using namespace std;
#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
#define INFTY 0x3f3f3f3f
#define maxn 200005
struct EDG 
{
 int v,nxt,w;
};
EDG edg[4000500];
int head[200500];
int d[200500];
int dis[100500];
bool vist[200500];
bool hash1[100500];
int N,M,C;
int cnt;
queue<int> pq;
void addedg(int x,int y,int w)
{
 edg[cnt].v=y;
 edg[cnt].w=w;
 edg[cnt].nxt=head[x];
 head[x]=cnt++;
}
void init()
{
 memset(head,-1,sizeof(head));
 memset(d,INFTY,sizeof(d));
 memset(vist,0,sizeof(vist));
 memset(hash1,0,sizeof(hash1));
 cnt=0;
}
void SPFA()
{
 while(!pq.empty())
 pq.pop();
 pq.push(1);
 vist[1]=1;
 int top,v,w,i;
 d[1]=0;
 while(!pq.empty())
 {
  top=pq.front();
  pq.pop();
  vist[top]=0;
  for(i=head[top];i!=-1;i=edg[i].nxt)
  {
   v=edg[i].v;
   w=edg[i].w;
   if(d[v]>d[top]+w)
   {
    d[v]=d[top]+w;
    if(!vist[v])
    {
     pq.push(v);
     vist[v]=1;
    }
   }
  }
 }
}
int main()
{
 int T,count1=1;
 cin>>T;
 while(T--)
 {
  scanf("%d%d%d",&N,&M,&C);
  init();
  for(int i=1;i<=N;i++)
  {
   scanf("%d",&dis[i]);
   hash1[dis[i]]=1;
  }
  for(int i=1;i<N;i++)  //层和层之间建双向边,把层抽象化为一个过渡点
  {
   if(hash1[i]&&hash1[i+1])
   {
    addedg(N+i,N+i+1,C);
    addedg(N+i+1,N+i,C);
   }
  }
  for(int i=1;i<=N;i++)    //层和点建边,当前层对当前点建边;点和层建边,建单向跨层边,点到层,不能层到点,节约空间和时间
  {  
   addedg(dis[i]+N,i,0);
   if(dis[i]>1)
    addedg(i,dis[i]+N-1,C);
   if(dis[i]<N)
    addedg(i,dis[i]+N+1,C);
  }
  for(int i=1;i<=M;i++)      //点和点之间建边
  {
   int x,y,z;
   scanf("%d%d%d",&x,&y,&z);
   addedg(x,y,z);
   addedg(y,x,z);
  }
  SPFA();
  if(d[N]<INFTY)
   printf("Case #%d: %d\n",count1++,d[N]);
  else
   printf("Case #%d: %d\n",count1++,-1);
 }
}

猜你喜欢

转载自blog.csdn.net/qq_41997978/article/details/83473586