题目大意:
有n个点,m条无向边,k条火车路。
火车路是从节点1直接通向节点的路。
问最多可以拆除多少条火车路。
思路:
1、首先,建图之后跑最短路 ,得到从节点1到其他个点的最短路。
2、然后将dis【i】<火车路花费的火车路拆除掉,因为显然这条火车路是不需要的,因为有更短的路径比火车路的路径更短
3、当dis【i】==火车路花费的时候,我们要记录节点i的最短路径入度,如果入度>1,那么就可以拆除当前火车路。因为这样就说明有两条及以上的路径能够从1到达节点i,那么这个火车路也是多余的。那么要如何记录节点i的最短路径入度呢?我们在松弛的时候加上两个语句:
if(dis【v】>dis【u】+w)in【v】=1;
if(dis【v】==dis【u】+w)in【v】++;
这样我们就能够统计下来节点i的最短路径条数。
4、玄学优先队列。我写的是SPFA,队列TLE on 45,栈TLE on 4,因为比较渣,堆写的很挫,写了发优先队列+Dij,也是TLE on45,最后百度了一下题解,竟然可以用优先队列水过数据.........................(难道优先队列真的能够优化算法?)
Ac代码;
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
using namespace std;
#define ll __int64
ll head[1000000];
struct node
{
ll from;
ll to;
ll next;
ll w;
}e[1515151];
ll in[15151515];
ll go[1515151];
ll val[1515151];
ll dis[1515155];
ll vis[1515155];
ll n,m,k,cont;
void add(ll from,ll to,ll w)
{
e[cont].to=to;
e[cont].w=w;
e[cont].next=head[from];
head[from]=cont++;
}
void SPFA(ll ss)
{
priority_queue<ll>s;
for(ll i=1;i<=n;i++)dis[i]=1000000000000000000;
memset(vis,0,sizeof(vis));
vis[ss]=1;
dis[ss]=0;
s.push(ss);
while(!s.empty())
{
ll u=s.top();
s.pop();
vis[u]=0;
for(ll i=head[u];i!=-1;i=e[i].next)
{
ll v=e[i].to;
ll w=e[i].w;
if(dis[v]>dis[u]+w)
{
in[v]=1;
dis[v]=dis[u]+w;
if(vis[v]==0)
{
vis[v]=1;
s.push(v);
}
}
else if(dis[v]==dis[u]+w)in[v]++;
}
}
}
int main()
{
while(~scanf("%I64d%I64d%I64d",&n,&m,&k))
{
cont=0;
memset(in,0,sizeof(in));
memset(head,-1,sizeof(head));
for(ll i=0;i<m;i++)
{
ll x,y,w;
scanf("%I64d%I64d%I64d",&x,&y,&w);
add(x,y,w);
add(y,x,w);
}
for(ll i=0;i<k;i++)
{
scanf("%I64d%I64d",&go[i],&val[i]);
add(1,go[i],val[i]);
add(go[i],1,val[i]);
}
ll output=0;
SPFA(1);
for(ll i=0;i<k;i++)
{
if(dis[go[i]]<val[i])output++;
else if(dis[go[i]]==val[i])
{
if(in[go[i]]>1)
{
in[go[i]]--;
output++;
}
}
}
printf("%I64d\n",output);
}
}