传送门: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);
}
}