Codeforces-450D-Jzzhu and Cities+dji
参考:https://blog.csdn.net/corncsd/article/details/38235973
传送门:http://codeforces.com/problemset/problem/450/D
题意:
有N个地方,M条线路,K条火车路(从1出发)。最多删掉多少火车路使1到每个点的最短路不变。
思路:
用dijkstra的思路,优先队列每次找出d最小的。先用火车路更新最小的d。当u的最短路确定的时候,若还是那个火车路,没有被别的路更新,说明这条火车路不能删,若这条路已经被更新过,说明火车路能删。
怎么判断一个点有没有被更新过呢,这里有个巧妙的方法,一开始把节点编号的负值加到优先队列中,如果最后确定最短路的时候还是负值说明没有被更新过。这个巧妙点还在于,如果存在两个的 first 负值相同,那么,编号为正的一定排在前面,就是说普通路的优先级会比铁路的高;
有一点要注意,更新条件是d[v]>=d[u]+w,因为如果出现和火车路长度相同的路也要更新,因为这样也可以把火车路删掉。
把d加入队列的时候可以加d的负值,因为默认的优先队列是先pop大的,负的最大就是正的最小。
ac代码:
#include <iostream> #include <queue> #include <cstdio> #include <vector> #include <cstring> using namespace std; #define pb push_back typedef long long ll; const ll INF = 1e18+9; const int maxn = 1e5+9; ll n, m, k; ll dis[maxn]; bool vis[maxn],vv[maxn]; vector< pair<ll, ll > >mp[maxn]; priority_queue< pair<ll ,ll > >q; int main(){ scanf("%lld%lld%lld",&n,&m,&k); memset(vv,0,sizeof(vv)); for(int i=1; i<=m; i++) { ll u, v; ll c; scanf("%lld%lld%lld",&u,&v,&c); mp[u].pb(make_pair(v,c)); mp[v].pb(make_pair(u,c)); } for(int i=1; i<=n; i++) dis[i] = INF, vis[i]=false; for(int i=1; i<=k; i++) { ll a; ll b; scanf("%lld%lld",&a,&b); dis[a] = min(dis[a],b); q.push(make_pair(-b,-a)); } ll ans = 0; dis[1] = 0; // vis[1] = true; q.push(make_pair(0 , 1)); while( !q.empty() ) { ll v = q.top().second; q.pop(); if(v<0) { v = -v; if(vis[v]) ans++; } if(vis[v])continue; vis[v]=1; for(int i = 0; i < mp[v].size(); i++) { ll tmp = mp[v][i].first; ll tmpc = mp[v][i].second; if( dis[tmp] >= dis[v] + tmpc ) { dis[tmp] = dis[v] + tmpc; q.push(make_pair(-1ll*dis[tmp], tmp)); } } } printf("%lld\n",ans); return 0; }
skr