写题
因为上一题做的也是修路题,这个比上一个还少了很多复杂的步骤所以只需要在上一题的代码上稍加改变就ok
需要注意的是题目的数组大小,之前我没改直接交了RE了然后随便瞄一眼题目范围把数组开到1000多点我就以为够了但是因为边数是k*(k-1)/2,而k<100,所以我数组要开到5000的样子
还有我初始化parent数组时,范围给错了,题意输入的村庄编号是从1开始到n,所以init初始化parent数组的范围也是要从1到n
一开始我也没仔细看题,以为和前两个修路一样的,直接按照给的样例来写输入格式,试了一下样例就过了一交WA,仔细看了题才知道这和之前还真有蛮大不同的,之前的都是确保连通的情况,这个题是不一定确保连通,如果还是在之前那个代码上修改的话,我的代码就是默认连通的情况了,样例过了纯属巧合
该题在输入路之后还要输入连个村庄编号s、t,要求的是从s到t的最短路径,但是是有s和t不通路的情况,这时就输出-1
该题可以用Floyd算法来解
Floyd算法(n^3)
Floyd算法定义了两个二维矩阵:
distance [v][w] 记录两点间的最短距离,path[v][w]记录两点间最小路径的中转站
例如
distance [v][w] =5,说明从点0到点3的最短路径为5
path [v][w] =1,说明0到3的最短路径的中转站是1,也就是说改最短路径的轨迹为0->1->3
改算法通过3重循环,k为中转点,v为起点,w为终点,循环比较distance [v][w] 和distance [v][k] + distance [k][w] 最小值,如果distance [v][k] + distance[k][w] 为更小值,则把distance[v][k] + distance[k][w] 覆盖保存在distance[v][w]中。(所以该算法的时间复杂度为n^3)
大概知道该怎么写了,因为题目不需要打印路径,所以我没有定义path二维数组,
初始化
memset(diatance,127,sizeof(diatance));
//将diatance初始化为一个很大的值
for(int i=0;i<n;i++){
diatance[i][i]=0;
//将城市本身到本身的路径设为0
}
这个简单题一直给我WA,我感觉思路也蛮对的啊
我输出了二维数组来看我发现,我初始化值为memset(diatance,127,sizeof(diatance));的话,在三层循环里,要是两个这么大的数相加的话会超int
(但是我初始为memset(diatance,999999,sizeof(diatance));的时候明明没超int咋还WA?不懂)
然后我在三层循环那里加了个判断,如果if(diatance[i][k]>inf && diatance[k][j]>inf) continue;也就防止了两个那么大的数相加超int的情况 em还是WA,看来问题不仅仅是这里
我忽略了一个情况,也就是存数据时,要是两点间不止一条路径,就得判断存最短的那一条
就是这样
for(int i=0;i<m;i++){
scanf("%d%d%d",&a,&b,&d);
if(diatance[a][b]>d){//考虑存数据时要是有两点间有不同的路,需要判断找出该两点的最短路
diatance[a][b]=d;
diatance[b][a]=d;
}
//导入数据,若是a-b没有直接的路径,那么diatance【a】【b】上就是那个很大的值
}
改了之后就ac了
Floyd核心代码
for(int k=0;k<n;k++)
for(int i=0;i<n;i++)
for(int j=0;j<n;j++){
if(diatance[i][k]>inf && diatance[k][j]>inf)
continue;
if(diatance[i][j]>diatance[i][k]+diatance[k][j]){
diatance[i][j]=diatance[i][k]+diatance[k][j];
//如果有i到j有其他路径比目前存的的距离小些,更新
}
}
Floyd算法TLE了,用些别的
Dijkstra算法(优先队列)
Dijkstra算法是类似于广度搜索,我之前写广搜又是用的队列,最短路算法可以用优先队列
优先队列:#include<queue>头文件里的 priority_queue<type>函数
思路相关联起来就会很简单,将初始点start放入队列,出队列并另now=start
将和now有路的点都存到队列里,优先队列会自动排序,然后最短的那条路会先出来,这个步骤依次循环,直到start=en时,也就是找到答案时停止循环,输出答案
写题时遇到的问题:
队列用的的结构体变量,那么优先队列里排序的时候,怎么知道是要比较哪个数来排序嘞?
哦要自己写比较函数了,这样
struct node
{
int topoint;
int d;
};
struct cmp
{
//结构体的优先队列中的比较函数,判断要反着写
bool operator()(node&x,node&y){
return x.d > y.d;
//小根堆
}
}
priority_queue <int,vector<node>,cmp> pq;
根据题意,题意还要输出最短路时的花费,怎么解决呢?
哦可以一开始的结构体再多一个来存花费
struct node
{
int topoint;
int d;
int p;
};
入队列的时候
void putnum(node now)
{
for(int i=1;i<=n;i++){
if(distance[now.topoint][i]<inf){
node temp;
temp.d = now.d+distance[now.topoint][i];
temp.p = now.p+cost[now.topoint][i];
now=temp
pq.push(now);
}
}
}
我服了编译的时候一堆编译错误,我不写这个方法了,我太傻了真的浪费我时间!!气死了
Dijkstra算法
和Floyd算法相比,这个算法的时间复杂度少些,Floyd是n^3,Dijkstra是n^2
因为Dijkstra算法只会算出start到end一条路的最短路
而Floyd是直接将任意两点间的最短路径都算出来了
相对来说这两个都很好理解,意思其实也差不多,只不过Dijkstra算法因为是只用算一条start到end的最短路,降低时间复杂度,可以用两个一维数组dis【】,cost【】来存所有以start为起点的路径的最短路和最少花费
ac核心代码段
for(int j=1;j<=n;j++){
if(!vis[j]&&diatance[now][j]<inf){
if(dis[j]>diatance[now][j]+dis[now]){
dis[j]=diatance[now][j]+dis[now];
c[j]=c[now]+cost[now][j];
}else if(dis[j]==diatance[now][j]+dis[now]){
if(c[j]>c[now]+cost[now][j])
c[j]=c[now]+cost[now][j];
}
}
}
这个题只要在上面那个代码的基础下,将cost【】【】,p,c【】删掉,然后起点为1,终点为 n
Dijkstra(1,n)就好了