今天的第一个题
也不知道折磨了我多久,还找了好几个同学问了
首先第一个就是对于数据的存储。
使用的方法是链式向前星
其中的原理和链表法和邻接表有点类似
4 6 1 1 2 2 2 3 2 2 4 1 1 3 5 3 4 3 1 4 4
借用题目中的数据
首先我们定义一个结构体
其中to存放他的目标点
length存放距离
next存放与他相邻的其他节点
然后在定义一个数组head
首先第一个数据1 2 2
对于第一个结构体他的to=2,length=2,next=【1】=0(当next为0时说明没有邻接的节点)
head[1]=1
第二个数据2 3 2
对于第二个结构体to=3,length=2,next=head【2】=0
head【2】=2
第三个数据2 4 1
对于第三个结构体to=4,length=1,next=head【2】=2
head【2】=3
第四个数据1 3 5
对于第四个结构体to=3,length=5,next=head【1】=1
head【1】=4
第五个数据3 4 3
对于第五个结构体to=4,length=3,next=head【3】=0;
head【3】=5
第六个数据1 4 4
对于第六个结构体to=4,length=4,next=head【4】=0;
head【4】=6;
其中存放的结构类似于这样
使用链表也是可以达到同样的效果
我最开始的时候用的就是链表
然后就是遍历最短路的方法了
以题目中给出的图为例
首先我们从起点出发
从1我们可以达到2,3,4
我们同样建立一个数组来存放最短路
因为1-1不需要移动,所以距离就为0
最开始我们可以从1-2,3,4
所以得出了一个新的表格
然后从接下来的表格中选择距离最短的哪一个也就是2
从节点2出发能够到达3 和4
因为我们要求的是从节点1到达各个节点的最短距离,
假设我们从2中转到3和4所需要的的距离就是4和3
明显比原理的距离更加的近,所以我们更新表格
然后从接下来的3和4中挑选路径最短的3,通过3我们能够中转到4,但是所需要的距离为8比原来的距离更远,所以不做更改
最后挑选4,节点4无法达到任意一个节点,所以最终答案就是
0 2 4 3
代码如下
#include<iostream>
using namespace std;
long long Max=2147483647;
int point,side,start;
long long result[1000000];
long long book[1000000];
struct Data
{
int to;
int length;
int next;
}data[1000000];
int head[1000000];
int top=0;
int add(int a,int b,int c)
{
top++;
data[top].to=b;
data[top].length=c;
data[top].next=head[a];
head[a]=top;
return 0;
}
int finding(int start)
{
for(int i=1;i<=point ;i++)
{
result[i]=Max;
book[i]=0;
}
result[start]=0;
int present=start;
while(book[present]==0)
{
book[present]=1;
long long mining=Max;
for(int i=head[present];i;i=data[i].next)
{
if(book[data[i].to]==0)
if(result[data[i].to]>result[present]+data[i].length)
result[data[i].to]=result[present]+data[i].length;
}
for(int i=1;i<=point;i++)
{
if(book[i]==0)
if(result[i]<mining)
{
mining=result[i];
present=i;
}
}
}
return 0;
}
int main()
{
cin>>point>>side>>start;
for(int i=1;i<=side;i++)
{
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
}
finding(start);
for(int i=1;i<=point ;i++)
cout<<result[i]<<' ';
}
这个题目足足折磨了我一整天
一共提交了我20次
第二个题
这个题目的解法感觉是和暴力破解是差不多的
啊哈算法里面也是介绍到了这种算法。
特别是题目还给出了对应关系
我们同样也建立一个这样的数组data
其中的对应关系,比如说data【1】【3】=1
说明1到3的危险程度为1
我们从1-3有两种方法一种是直接从1-3,还有一种就是通过中间节点2
从1到2再到3首先通过中间节点的危险程度为data【1】【2】+data【2】【3】=7。显然危险程度更高,如果我们遇到危险程度更低的我们就交换,
如果我们要从2-1直接到达的危险程度为5
从节点3绕过去的话就是data【2】【3】+data【3】【1】=3
危险程度更低,所以我们就把data【2】【1】变成3;通过这样的比较变换,优化所有的路线
最终的代码实现如下
#include<iostream>
using namespace std;
int n,m;
int point[1000];
int data[10004][10004];
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
cin>>point[i];
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
cin>>data[i][j];
}
}
int count=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
for(int k=1;k<=n;k++)
{
//选择更优的路线
if(data[j][k]>data[j][i]+data[i][k])
{
data[j][k]=data[j][i]+data[i][k];
}
}
}
}
//根据求出的最优路线来走对应路线
for(int i=2;i<=m;i++)
{
count+=data[point[i-1]][point[i]];
}
cout<<count;
}
其中用到的算法就是Floyd算法
其中主要就是用到三个for循环,第一个代表中转节点,下面的两层就是不同过节点
最终判断通过节点更好还是不通过节点更好,选择最优路线,最终求得解
第三个题
如果去掉题目中求终点并改变无向图为有向图就和我今天写的第一题一模一样
求重点很简单就是输出result【重点】就可以了,
有向图变成无向图该怎么处理呢。
其实也很简单。
有向图就是只可以过去,不可以回来
无向图就是既可以过去,又可以回来。
那么我们再有向图的基础上加一个回来的路线不就可以了么
所以我也是直接在第二题的代码上稍加修改就可以了
最终代码如下
#include<iostream>
using namespace std;
long long Max=2147483647;
int point,side,start;
int ending;
long long result[1000000];
long long book[1000000];
struct Data
{
int to;
int length;
int next;
}data[1000000];
int head[1000000];
int top=0;
int add(int a,int b,int c)
{
top++;
data[top].to=b;
data[top].length=c;
data[top].next=head[a];
head[a]=top;
return 0;
}
int finding(int start)
{
for(int i=1;i<=point ;i++)
{
result[i]=Max;
book[i]=0;
}
result[start]=0;
int present=start;
while(book[present]==0)
{
book[present]=1;
long long mining=Max;
for(int i=head[present];i;i=data[i].next)
{
if(book[data[i].to]==0)
if(result[data[i].to]>result[present]+data[i].length)
result[data[i].to]=result[present]+data[i].length;
}
for(int i=1;i<=point;i++)
{
if(book[i]==0)
if(result[i]<mining)
{
mining=result[i];
present=i;
}
}
}
return 0;
}
int main()
{
cin>>point>>side>>start>>ending;
for(int i=1;i<=side;i++)
{
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
add(b,a,c);
}
finding(start);
cout<<result[ending];
}
其中的核心代码和中心思想基本都没有改变
第四个题
这个题也是前面加勒比海盗的变形
只不过稍微麻烦了一点,有去和回来
如果去是data[i][j]的话回来就是data[j][i]
最终的代码如下
#include<iostream>
using namespace std;
int n,m;
int data[1003][1003];
int main()
{
cin>>n>>m;
for(int i=0;i<=n;i++)
{
for(int j=0;j<=n;j++)
{
data[i][j]=99999999;
}
}
for(int i=1;i<=m;i++)
{
int a,b,c;
cin>>a>>b>>c;
data[a][b]=min(data[a][b],c);
}
long long count=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
for(int k=1;k<=n;k++)
{
if(data[j][k]>data[j][i]+data[i][k])
{
data[j][k]=data[j][i]+data[i][k];
}
}
}
}
for(int i=2;i<=n;i++)
{
count+=data[1][i]+data[i][1];
}
cout<<count;
}
其中还要稍微考虑一下重复边,就是可能一条大路一条小路,要选择最短的那一条路