E - Charles in Charge ( dijstra+二分 )
题意:一张城市网,有n个城市,m条高速公路,只有城里有充电站、公路上没有充电站,充电时间不计,汽车电池有多少电量就只能走多少路,男人每天需要在1号家和m好公司之间奔波,男人为了省钱需要买尽可能小的汽车电池,女人为了让男人尽快的回家让男人走最近的路。经过妥协,男人必须走不超过最短路长度x%的路,在这基础上男人能买的最小电池为多少?
思路:先跑一边dijstra,求出男人可以走的最远的路是多长,然后二分枚举电池容量,比如当二分枚举到电池容量为18时,那么男人不能走超过18距离的高速公路( 因为他走在半路就没电了 ),这样我们就把原先图上大于18的路都给删掉再跑一遍dijstra就好了。但是删边并不好实现,重新建一张图时间复杂度也太高。最优解决方案就是在dijstra里加上对边的判断。
代码:
#include <bits/stdc++.h>
#define int long long
#define inf 0x3f3f3f3f
using namespace std;
struct nod {
int u,v,w;
}a[100005];
struct no {
int to,w,nxt;
}e[100005];
typedef struct node {
int v,date;
}ty;
int n,m,x,cnt;
int dis[100005];
int via[100005];
int head[100005];
int MinDis;
ty t,d;
bool operator<( const ty &a, const ty &b ) {
return a.date>b.date;
}
void addage( int u, int v, int w )
{
e[cnt].to = v;
e[cnt].w = w;
e[cnt].nxt = head[u];
head[u] = cnt++;
}
int dijstra(int isp, int mid)
{
priority_queue<ty> Q;
memset(dis,inf,sizeof(dis));
memset(via,0,sizeof(via));
dis[1] = 0;
t.v = 1; t.date=0;
Q.push(t);
while ( !Q.empty() ) {
if ( via[Q.top().v]==1 ) {
Q.pop();
continue;
}
t = Q.top(); Q.pop();
int u = t.v;
dis[u] = t.date;
via[u] = 1;
for ( int i =head[u]; i!=-1; i=e[i].nxt ) {
int j = e[i].to;
if ( e[i].w<=mid ) { // 把不符合条件的路删掉
if ( via[j]==0 && dis[j]>dis[u]+e[i].w ) {
dis[j] = dis[u]+e[i].w;
d.v = j; d.date = dis[j];
Q.push(d);
}
}
}
}
if ( isp==1 ) {
MinDis = dis[n]*( (100+x)/100.0 );
return 1;
}
if ( dis[n]<=MinDis ) {
return 1;
}
else return 0;
//cout << dis[n] << " " << now[n] << endl;
}
int solve( int mid )
{
return dijstra(0,mid);
}
signed main()
{
cin >> n >> m >> x;
for ( int i=0; i<m; i++ ) scanf("%lld %lld %lld",&a[i].u,&a[i].v,&a[i].w);
memset(head,-1,sizeof(head)); cnt=0;
for ( int i=0; i<m; i++ ) {
addage( a[i].u, a[i].v, a[i].w );
addage( a[i].v, a[i].u, a[i].w );
}
int ans;
dijstra(1,inf);
int left = 0, right = 1000000003;
while ( left<=right ) {
int mid = (left+right)/2;
if ( solve(mid)==1 ) {
ans = mid;
right = mid-1;
}
else {
left = mid+1;
}
}
cout << ans << endl;
return 0;
}