2 2 1000 0 0 2000 1000 1000 0 2000 0 0 1000 0 0 2000 1000
2 impossible
解题思路:
题意为在一个二维坐标中,给出起点,终点,以及n个加油站的坐标,车子从起点出发,且是加满油的,车子始终保持直线行驶,且只能在起点,加油站或终点之间行驶,每次经过一个加油站都必须加满油,且加满油只有最多可以走L长度,问要从起点走到终点,如果可以的话,最少经过多少加油站,如果不可以,输出impossible
一开始的思路是,一共n=n+2个节点(包含起点和终点),那么则有 n*(n-1)/2条边,再这里边挑出可行边,即边的长度<=给定的L,然后另可行边的权值为1,建立邻接矩阵,跑一遍最短路就可以了,最后答案为dis[n]-1。
但这种思路细节没有考虑到,比如起点坐标 0,0 第一个加油站坐标 3,0 第二个 4,0 第三个 5,0 ,L=6,可以发现这四个点在同一条直线上,而且从起点到第三个加油站相连的边长度也小于L,是可行边,按照上边的思路则该边的权值为1,但是第二个第三个加油站都在该边上,要想从起点到达第三个加油站,就必须经过第一个和第二个加油站,那么权值应该是3,而不是前面所说的1.
所以前面犯的错误就是加边加多了,如果和第i个节点组成的边中有斜率一样的,则这些边共线,那么只能连接最短的边,其他共线的边均舍去。
方法为每条边均为一个结构体,其中保存终点和边的斜率及边长,对每个节点都有一个结构体类型的vector,保存以该节点为起点的所有边,当找到一个可行边时,去里面找看看有没有与其斜率相同的,如果有,再比较两条边的长度,保存长度小的边,去掉另外一条边,当没有斜率相同的话,直接加边就可以了。加边也就是使邻接矩阵mp[i][j]=1,去边也就是令mp[i][j]=inf。这样建好图,跑一下最短路就可以了,答案为dis[n]-1.
代码:
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <stdlib.h>
#include <cmath>
#include <iomanip>
#include <vector>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <cctype>
using namespace std;
#define ll long long
const int maxn=1010;
const int inf=0x3f3f3f3f;
int dis[maxn];
bool vis[maxn];
int n;
int L;
int mp[maxn][maxn];
struct Node
{
int x,y;
}node[maxn];
struct Edge//边的结构体,指向谁,长度是多少
{
int to;
int up;
int down;//斜率的分子,分母
double len;
};
vector<Edge>vc[maxn];//每个点都有一个vector,用来存与该点相连的可行边
double distan(Node a,Node b)
{
return sqrt(double((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)));
}
void dijkstra(int start)
{
//**第一步:初始化,dis[]为最大,vis均为0(都未加入集合)
memset(dis,inf,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[start]=0; //注意不能写vis[start]=1,因为这时候第一个节点还没有被访问,下面循环中,第一个选择的就是第一个节点,切记</span>
//**第二步:找dis[]值最小的点,加入集合,并更新与其相连的点的dis[]值
//一开始集合里没有任何点,下面的循环中,第一个找到的点肯定是源点
for(int i=1;i<=n;i++)
{
//寻找dis[]最小的点,加入集合中
int MinNumber,Min=inf;//MinNumber为dis[]值最小的点的编号
for(int j=1;j<=n;j++)
{
if(dis[j]<Min&&!vis[j])
{
Min=dis[j];
MinNumber=j;
}
}
//找到dis[]最小的点,加入集合,更新与其相连的点的dis值
vis[MinNumber]=1;
for(int j=1;j<=n;j++)
if(dis[MinNumber]+mp[MinNumber][j]<dis[j])
dis[j]=dis[MinNumber]+mp[MinNumber][j];
}
}
int main()
{
int t;scanf("%d",&t);
while(t--)
{
for(int i=0;i<maxn;i++)
vc[i].clear();
scanf("%d%d",&n,&L);
n+=2;
scanf("%d%d",&node[1].x,&node[1].y);
scanf("%d%d",&node[n].x,&node[n].y);
for(int i=2;i<=n-1;i++)
scanf("%d%d",&node[i].x,&node[i].y);
memset(mp,inf,sizeof(mp));
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
{
double l=distan(node[i],node[j]);//之间的距离
if((L-l)>=0)//首先看是否满足初步条件,,可行边
{
int up=node[j].y-node[i].y;
int down=node[j].x-node[i].x;//该点的斜率
bool ok=0;//看能不能找到斜率相同的边
for(int k=0;k<vc[i].size();k++)
{
// if((up*vc[i][k].down==down*vc[i][k].up&&up!=0&&down!=0&&up!=0&&vc[i][k].down!=0&&vc[i][k].up!=0)||(up==0&&vc[i][k].up==0))
if(up*vc[i][k].down==down*vc[i][k].up)
{//两种情况,斜率不存在,up全部为0,斜率存在
// cout<<"i "<<i<<"j "<<j<<endl;
ok=1;//找到了斜率相同的可行边
if(l<vc[i][k].len)//斜率相同的两条边,目前边的长度比已有边的长度小,则去掉已有边,加入目前边
{
mp[i][vc[i][k].to]=inf;
mp[vc[i][k].to][i]=inf;
mp[i][j]=1;
mp[j][i]=1;
Edge edge;
edge.to=j;
edge.up=up;
edge.down=down;
edge.len=l;
vc[i].push_back(edge);
}
break;
}
}
if(!ok)//找不到斜率相同的,则加入新边
{
Edge edge;
edge.to=j;
edge.up=up;
edge.down=down;
edge.len=l;
mp[i][j]=1;
mp[j][i]=1;
vc[i].push_back(edge);
}
}
}
dijkstra(1);
if(dis[n]==inf)
printf("impossible\n");
else
printf("%d\n",dis[n]-1);
}
return 0;
}
[ACM] HDU 4885 TIANKENG’s travel (特殊建图,最短路)
原文地址:http://blog.csdn.net/sr_19930829/article/details/42679389