1. 程式人生 > >【NOIP模擬】距離

【NOIP模擬】距離

題面

對夢想的持續追求讓實力本身很弱的David一天天變強。
他最終考上了自己所喜歡的大學。
北京有啥好大學想必大家都比我清楚吧。

成都到北京之間有 座城市,城市和城市之間由 條雙向通行的道路相連,城市和城市之間兩兩可達。通過第條道路需要交納 的過路費。Acid公司很喜歡有夢想的年輕人,他們決定對前往高校上學的學生的路費予以補助。補助的方式是,在一條路徑

上,你只需要交納路費最貴的前 條道路的費用就可以了。

如果路徑經過了小於 條道路則不補助(這麼近你自己給嘛~)你的任務是求出David到達夢想中的學校所需要的最小路費。

分析

因為昨晚上才做了一個二分的題,因此拿著就開始二分,然後又是染0和1。。好吧掛得只有15分。。。

就像出題人所言,你看這資料範圍如此小,怎麼會是二分呢??二分的題的n怎麼也是跟了4個0的吧??

正確姿勢

列舉答案中最大的不收費邊設其價值為valx,然後把所有的邊的邊權減去valx,不足 valx的設為0,跑最短路,最後把答案加上k*valx ,然後再對所有的情況取min就是最優解

我們設最短路徑中有x條邊大於k

當 x取到k的時候,答案是正確的。剛好k條邊收了費。

而如果最短路徑中有大於k條邊是正數,我們將從大到小的第y條邊的邊權作為新的valx,並令其為valy

設原先的最短路為 M,以 valy作為答案,求出的答案將會變為 M-(valy-valx)*x+valy*k這個答案是比M+valx

*k小的

證明很簡單,抓住valy>valx,x>k,即可,不再贅述。

程式碼

#include<bits/stdc++.h>
using namespace std;
#define N 3030
#define mp make_pair
#define ll long long
#define INF 0x7fffffff7fffffff
typedef pair<ll,ll> pii;
ll n,m,k,cnt,ans=INF;
ll vis[N],d[N],first[N];
struct email
{
    ll u,v,w,o;
    ll nxt;
}e[N
*4]; inline void add(ll u,ll v,ll o) { e[++cnt].nxt=first[u];first[u]=cnt; e[cnt].u=u;e[cnt].v=v;e[cnt].o=o; } priority_queue<pii>q; ll get() { memset(d,0x3f,sizeof(d)); memset(vis,0,sizeof(vis)); while(!q.empty())q.pop(); d[1]=0; q.push(mp(-d[1],1)); while(!q.empty()) { pii x=q.top();q.pop(); ll u=x.second; if(vis[u])continue;vis[u]=1; for(ll i=first[u];i;i=e[i].nxt) { ll v=e[i].v,w=e[i].w; if(d[v]>d[u]+w&&d[u]+w<ans) d[v]=d[u]+w,q.push(mp(-d[v],v)); } } return d[n]; } int main() { scanf("%lld%lld%lld",&n,&m,&k); for(ll i=1;i<=m;i++) { ll u,v,o; scanf("%lld%lld%lld",&u,&v,&o); add(u,v,o);add(v,u,o); } for(ll i=0;i<=cnt;i++) { ll del=e[i].o; for(ll j=1;j<=cnt;j++)e[j].w=max((ll)0,e[j].o-del); ans=min(ans,get()+k*del); } printf("%lld\n",ans); return 0; }