HDU3499 Flight spfa

传送门:HDU 3499

题意:有一个有向图,你要从特定的城市A飞到城市B去.给你这个图的所有边(航班)信息.但是你手上有一张卡,可以使得某一趟航班的价格减半.现在的问题是你从A到B的最小费用是多少?

这个题A了的时候好激动啊。。虽然思想是听学长讲的,但是代码全程自己实现啊,虽然中间搜了一小下下题解,但是并没有什么卵用,因为网上本来这个题题解就少,大部分还是什么分层法写的,打算一会儿去学学。。

解法:以下分析转载:首先要知道这条如果让一条原本是最短路径(假设总距离为x)上最长的边变成半价,最终求得的解不一定是最优的。因为假如现在有另外一条路径,假设该路径距离为x+1。且这条路径上只有5条边,4条长为1的边,但是1条长为x-3的边。如果我们让这条路径的x-3边变成半价是不是能得到更好的结果?


其实上面的分析前半部分自己想想也能知道,但是明知道不对也不知道该怎么做啊。。学长当时讲的思路是正向建图,将起点到每个点的最短路计算并保存,反向建图,将终点到各个点的最短路保存下来,然后枚举每条边,假设这条边的端点为u和v,那么经过这条边并满足题意的最短路=起点到u的最短路+v到终点的最短路+这条边的权值w/2.

不明白的再自己想想吧。。


然后就是写完了一开始交无限CE,交GCC说什么头文件不存在,交C++说变量名和库函数名重复,交G++的错误直接看不懂。。然后从CE改成了WA,搜一发题解发现是自己建图的时候建错了,一开始居然建成了无向图。。然后增加了一个vector稍微改了改成了RE。。发现是新加的vector没有清空。。历经千辛万苦终于A了,给我激动的啊。。

ps:代码写的有点丑,而且写了两次spfa,虽然可以直接复制但是很不舒服啊,看到网上有妙用结构体的,只用写一遍就好了,就类似于把整个子函数封装起来,好厉害的样子。

#include <iostream>
#include <cstdio>
#include <cstring>
#include<algorithm>
#include<map>
#include<vector>
#include<queue>
#define ll long long
#define inf 1e17
using namespace std;
struct node{
	int v;
	ll w;
};
struct Edge//将边全部存到结构体数组中以备枚举的时候用
{
	int u,v;
	ll w;
}edge[500005];
int n,m;
ll dis[100005],start[100005],End[100005];//start存起点到各个点的最短路,end存终点的
map<string,int> p;
vector<node>mp[100005];
vector<node>mp1[100005];
void spfa(int s)//正向
{
	int book[100005];
	queue<int>q;
	while(!q.empty())
	q.pop();
	for(int i=0;i<=n;i++)
	dis[i]=inf;
	memset(book,0,sizeof(book));
	book[s]=1;
	dis[s]=0;
	q.push(s);
	while(!q.empty())
	{
		int k=q.front();
		q.pop();
		int j=mp[k].size();
		for(int i=0;i<j;i++)
		{
			int d=mp[k][i].v;
			if(dis[d]>dis[k]+mp[k][i].w)
			{
				dis[d]=dis[k]+mp[k][i].w;
				if(!book[d])
				{
					q.push(d);
					book[d]=1;
				}	
			}
		}
		book[k]=0;
	}
}
void spfa1(int s)//反向
{
	int book[100005];
	queue<int>q;
	while(!q.empty())
	q.pop();
	for(int i=0;i<=n;i++)
	dis[i]=inf;
	memset(book,0,sizeof(book));
	book[s]=1;
	dis[s]=0;
	q.push(s);
	while(!q.empty())
	{
		int k=q.front();
		q.pop();
		int j=mp1[k].size();
		for(int i=0;i<j;i++)
		{
			int d=mp1[k][i].v;
			if(dis[d]>dis[k]+mp1[k][i].w)
			{
				dis[d]=dis[k]+mp1[k][i].w;
				if(!book[d])
				{
					q.push(d);
					book[d]=1;
				}	
			}
		}
		book[k]=0;
	}
}
int main()
{	
	char s[12],c[12];	
	while(~scanf("%d%d",&n,&m))
	{
		int cnt=1;
		ll d;
		node t;
		Edge e;
		p.clear();
	for(int i=0;i<100005;i++)
	{
		mp[i].clear();
		mp1[i].clear();
	}
	for(int i=0;i<m;i++)
	{
		scanf("%s%s%lld",s,c,&d);		
		if(!p.count(s))//map离散化
		p[s]=cnt++;
		if(!p.count(c))
		p[c]=cnt++;
		edge[i].u=p[s];
		edge[i].v=p[c];
		edge[i].w=d;
		t.v=p[s];
		t.w=d;
		mp1[p[c]].push_back(t);//反向建图 
		t.v=p[c];
		mp[p[s]].push_back(t);//正向建图 
	}
	int st,en;
	scanf("%s%s",s,c);
	if(!p.count(s))//注意这里也要用map处理一下
	p[s]=cnt++;
	if(!p.count(c))
	p[c]=cnt++;
	st=p[s];
	en=p[c];
	spfa(st);
	if(dis[en]==inf)
	{
		printf("-1\n");
		continue;
	}
	memcpy(start,dis,sizeof(start));
	spfa1(en);
	memcpy(End,dis,sizeof(End));
	ll ans=inf;
	for(int i=0;i<m;i++)//枚举找最短路
	{
		int u=edge[i].u,v=edge[i].v;
		ll w=edge[i].w;
		ans=min(ans,start[u]+End[v]+w/2);
	}
	printf("%lld\n",ans);
	}		
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值