【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;
}

猜你喜欢

转载自www.cnblogs.com/NSD-email0820/p/9888321.html