题面
对梦想的持续追求让实力本身很弱的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 ",ans); return 0; }