problem:Part Acquisition
题目描述:
The cows have been sent on a mission through space to acquire a new milking machine for their barn. They are flying through a cluster of stars containing N (1 <= N <= 50,000) planets, each with a trading post.
The cows have determined which of K (1 <= K <= 1,000) types of objects (numbered 1…K) each planet in the cluster desires, and which products they have to trade. No planet has developed currency, so they work under the barter system: all trades consist of each party trading exactly one object (presumably of different types).
The cows start from Earth with a canister of high quality hay (item 1), and they desire a new milking machine (item K). Help them find the best way to make a series of trades at the planets in the cluster to get item K. If this task is impossible, output -1.
给出N条边,单向的。再给出数字K.问从1号点到K点号,最短路上经过多少个点。
输入格式:
- Line 1: Two space-separated integers, N and K.
- Lines 2…N+1: Line i+1 contains two space-separated integers, a_i and b_i respectively, that are planet i’s trading trading products. The planet will give item b_i in order to receive item a_i.
输出格式:
- Line 1: One more than the minimum number of trades to get the milking machine which is item K (or -1 if the cows cannot obtain item K).
样例输入:
6 5
1 3
3 2
2 3
3 1
2 5
5 4
样例输出:
4
我们先得把输入格式翻译一下:
*第1行:两个以空格分隔的整数,N和K.
*第2…N + 1行:第i + 1行包含两个空格分隔的整数,a_i和b_i,分别是行星i的交易交易产品。行星将给出项目b_i以便接收项目a_i。
看到这,我们就可以画样例图了:
这就是这题给的样例:
(注意,这题他的连线是单向的)
从1到5经过了:1----3----2----5四个点,所以答案是4.
那我们想想,这个题目怎么做呢?想一下上篇博客写的方法,费洛伊德算法可以做出来吗?(详见https://blog.csdn.net/qq_42884520/article/details/86678329),答案应该是不行的,至少本蒟蒻是做不出的 ,因为费洛伊德算法常针对的是双向的线,而此题是单向的,所以我们得想其它的方法了:没错,这就是今天要使用的Dijkstra算法,那什么是Dijkstra算法呢?让我们查查百度百科:(https://baike.baidu.com/item/迪杰斯特拉算法/4049057?fromtitle=Dijkstra算法&fromid=215612&fr=aladdin)
看完后是不是一脸懵比?没事,让我们看看一个例子就好啦吗:
我们看看这个图,我们怎么办,很简单,我们写一个循环,第一轮先把dis[1]打上标记:做一个替换 dis[2]=2,dis[3]=4,dis[4]=7;
第二轮我们把dis[2]打上标记,使得:dis[3]=2+1=3 dis[5]=2+2=4;
第三轮把dis[3]打上标记:使得:dis[4]=4;
接下来的循环把4,5都打上标记,这样,整个图的最短路径都能求出来啦
但是这个方法不能用来处理带有负数的值,不然就会产生错误答案,不信自己去推一推。
这个算法的思想是什么呢:
就是从一个点出发,到一个点的最短路径必然会经过一个中转点,我们只要求出中转点的最短路径,答案就显而易见了。
代码实现:
(设起点为s,pre[v]为v的前驱节点,用来输出路径)
1.初始化:dis[v]=0x7f,dis[s]=0;pre[s]=0;
2.for(int i=1;i<=n;i++)
1.找一个未访问过的u点,使得dis[u]最小;
2.标记u
3.for与u相连的每个未确定最短路径的顶点v。
if(dis[u]+w[u][v]<dis[v])
{
dis[v]=dis[u]+w[u][v];
pre[v]=u;
}
3.结束。
有了以上这些基础,在重新回去看看题,相信你可以做出来的
下面附上AC代码和解析:
#include<cstdio>
#include<iostream>
#define INF 10000005
using namespace std;
int map[1005][1005],dis[1005],path[1005],vis[1005];
int n,k;
void read(int &x)
{
char ch;
bool ok;
for(ok=0,ch=getchar(); !isdigit(ch); ch=getchar()) if(ch=='-') ok=1;
for(x=0; isdigit(ch); x=x*10+ch-'0',ch=getchar());
if(ok) x=-x;
}
void dijkstra()
{
for(int i=1;i<=k;i++)
{
dis[i]=map[1][i];
if(i!=1&&map[1][i]<INF) //记录当前能到达的点的前驱
path[i]=1;
}
vis[1]=1;
int min,v;
for(int i=1;i<k;i++)
{
min=INF;
v=1;
for(int j=1;j<=k;j++)
{
if(!vis[j]&&dis[j]<min)
{
min=dis[j];
v=j;
}
}
vis[v]=1;
for(int j=1;j<=k;j++)
{
if(!vis[j]&&dis[j]>dis[v]+map[v][j])
{
dis[j]=dis[v]+map[v][j];
path[j]=v; //记录到达j点的最短路径的前驱
}
}
}
}
int main()
{
read(n);read(k);
for(int i=1;i<=k;i++)
{
for(int j=1;j<=k;j++)
{
//对vis数组,path数组,map数组进行初始化
map[i][j]=INF;
}
vis[i]=0;
path[i]=-1;
}
int u,v;
for(int i=1;i<=n;i++)
{
read(u);read(v);
map[u][v]=1; //因为要计算通过的顶点的个数,所以把map初始化1
}
dijkstra();
if(dis[k]==INF)
{
//判断两点间是否能够到达
printf("-1\n");
}
else
{
printf("%d\n",dis[k]+1); //输出需要经过的顶点的个数 ,顶点数是边的数目加1
}
return 0;
}
谢谢各位大佬的观看,谢谢,谢谢。